Commit 086d8f3a by christosts Committed by Oliver Woodman

Contract test for TransferListener callbacks

PiperOrigin-RevId: 357190780
parent b303ecea
...@@ -28,6 +28,7 @@ dependencies { ...@@ -28,6 +28,7 @@ dependencies {
androidTestImplementation 'androidx.test:rules:' + androidxTestRulesVersion androidTestImplementation 'androidx.test:rules:' + androidxTestRulesVersion
androidTestImplementation 'androidx.test:runner:' + androidxTestRunnerVersion androidTestImplementation 'androidx.test:runner:' + androidxTestRunnerVersion
androidTestImplementation 'androidx.multidex:multidex:' + androidxMultidexVersion androidTestImplementation 'androidx.multidex:multidex:' + androidxMultidexVersion
androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito:' + dexmakerVersion
// Instrumentation tests assume that an app-packaged version of cronet is // Instrumentation tests assume that an app-packaged version of cronet is
// available. // available.
androidTestImplementation 'org.chromium.net:cronet-embedded:72.3626.96' androidTestImplementation 'org.chromium.net:cronet-embedded:72.3626.96'
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.upstream; package com.google.android.exoplayer2.upstream;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.testutil.DataSourceContractTest; import com.google.android.exoplayer2.testutil.DataSourceContractTest;
...@@ -42,12 +43,14 @@ public class CacheDataSourceContractTest extends DataSourceContractTest { ...@@ -42,12 +43,14 @@ public class CacheDataSourceContractTest extends DataSourceContractTest {
@Rule public final TemporaryFolder tempFolder = new TemporaryFolder(); @Rule public final TemporaryFolder tempFolder = new TemporaryFolder();
private Uri simpleUri; private Uri simpleUri;
private FileDataSource fileDataSource;
@Before @Before
public void setUp() throws IOException { public void setUp() throws IOException {
File file = tempFolder.newFile(); File file = tempFolder.newFile();
Files.write(Paths.get(file.getAbsolutePath()), DATA); Files.write(Paths.get(file.getAbsolutePath()), DATA);
simpleUri = Uri.fromFile(file); simpleUri = Uri.fromFile(file);
fileDataSource = new FileDataSource();
} }
@Override @Override
...@@ -71,6 +74,12 @@ public class CacheDataSourceContractTest extends DataSourceContractTest { ...@@ -71,6 +74,12 @@ public class CacheDataSourceContractTest extends DataSourceContractTest {
Util.createTempDirectory(ApplicationProvider.getApplicationContext(), "ExoPlayerTest"); Util.createTempDirectory(ApplicationProvider.getApplicationContext(), "ExoPlayerTest");
SimpleCache cache = SimpleCache cache =
new SimpleCache(tempFolder, new NoOpCacheEvictor(), TestUtil.getInMemoryDatabaseProvider()); new SimpleCache(tempFolder, new NoOpCacheEvictor(), TestUtil.getInMemoryDatabaseProvider());
return new CacheDataSource(cache, new FileDataSource()); return new CacheDataSource(cache, fileDataSource);
}
@Override
@Nullable
protected DataSource getTransferListenerDataSource() {
return fileDataSource;
} }
} }
...@@ -17,8 +17,11 @@ package com.google.android.exoplayer2.testutil; ...@@ -17,8 +17,11 @@ package com.google.android.exoplayer2.testutil;
import static com.google.android.exoplayer2.util.Assertions.checkArgument; import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
...@@ -26,6 +29,7 @@ import androidx.annotation.RequiresApi; ...@@ -26,6 +29,7 @@ import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
...@@ -36,6 +40,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -36,6 +40,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mockito;
/** /**
* A collection of contract tests for {@link DataSource} implementations. * A collection of contract tests for {@link DataSource} implementations.
...@@ -60,6 +67,15 @@ public abstract class DataSourceContractTest { ...@@ -60,6 +67,15 @@ public abstract class DataSourceContractTest {
protected abstract DataSource createDataSource() throws Exception; protected abstract DataSource createDataSource() throws Exception;
/** /**
* Returns the {@link DataSource} that will be included in the {@link TransferListener} callbacks
* if different from the {@link DataSource} under test, otherwise null.
*/
@Nullable
protected DataSource getTransferListenerDataSource() {
return null;
}
/**
* Returns {@link TestResource} instances. * Returns {@link TestResource} instances.
* *
* <p>Each resource will be used to exercise the {@link DataSource} instance, allowing different * <p>Each resource will be used to exercise the {@link DataSource} instance, allowing different
...@@ -241,6 +257,66 @@ public abstract class DataSourceContractTest { ...@@ -241,6 +257,66 @@ public abstract class DataSourceContractTest {
} }
} }
@Test
public void transferListenerCallbacks() throws Exception {
ImmutableList<TestResource> resources = getTestResources();
Assertions.checkArgument(!resources.isEmpty(), "Must provide at least one test resource.");
for (int i = 0; i < resources.size(); i++) {
additionalFailureInfo.setInfo(getFailureLabel(resources, i));
DataSource dataSource = createDataSource();
FakeTransferListener listener = spy(new FakeTransferListener());
dataSource.addTransferListener(listener);
InOrder inOrder = Mockito.inOrder(listener);
@Nullable DataSource callbackSource = getTransferListenerDataSource();
if (callbackSource == null) {
callbackSource = dataSource;
}
DataSpec reportedDataSpec = null;
boolean reportedNetwork = false;
TestResource resource = resources.get(i);
DataSpec dataSpec = new DataSpec.Builder().setUri(resource.getUri()).build();
try {
dataSource.open(dataSpec);
// Verify onTransferInitializing() and onTransferStart() have been called exactly after
// DataSource.open().
ArgumentCaptor<DataSpec> dataSpecArgumentCaptor = ArgumentCaptor.forClass(DataSpec.class);
ArgumentCaptor<Boolean> isNetworkArgumentCaptor = ArgumentCaptor.forClass(Boolean.class);
inOrder
.verify(listener)
.onTransferInitializing(
eq(callbackSource),
dataSpecArgumentCaptor.capture(),
isNetworkArgumentCaptor.capture());
reportedDataSpec = dataSpecArgumentCaptor.getValue();
reportedNetwork = isNetworkArgumentCaptor.getValue();
inOrder
.verify(listener)
.onTransferStart(callbackSource, castNonNull(reportedDataSpec), reportedNetwork);
inOrder.verifyNoMoreInteractions();
if (resource.isEndOfInputExpected()) {
Util.readToEnd(dataSource);
} else {
Util.readExactly(dataSource, resource.getExpectedBytes().length);
}
// Verify sufficient onBytesTransferred() callbacks have been triggered before closing the
// DataSource.
assertThat(listener.bytesTransferred).isEqualTo(resource.getExpectedBytes().length);
} finally {
dataSource.close();
inOrder
.verify(listener)
.onTransferEnd(callbackSource, castNonNull(reportedDataSpec), reportedNetwork);
inOrder.verifyNoMoreInteractions();
}
additionalFailureInfo.setInfo(null);
}
}
/** Build a label to make it clear which resource caused a given test failure. */ /** Build a label to make it clear which resource caused a given test failure. */
private static String getFailureLabel(List<TestResource> resources, int i) { private static String getFailureLabel(List<TestResource> resources, int i) {
if (resources.size() == 1) { if (resources.size() == 1) {
...@@ -344,4 +420,24 @@ public abstract class DataSourceContractTest { ...@@ -344,4 +420,24 @@ public abstract class DataSourceContractTest {
} }
} }
} }
/** A {@link TransferListener} that only keeps track of the transferred bytes. */
public static class FakeTransferListener implements TransferListener {
private int bytesTransferred;
@Override
public void onTransferInitializing(DataSource source, DataSpec dataSpec, boolean isNetwork) {}
@Override
public void onTransferStart(DataSource source, DataSpec dataSpec, boolean isNetwork) {}
@Override
public void onBytesTransferred(
DataSource source, DataSpec dataSpec, boolean isNetwork, int bytesTransferred) {
this.bytesTransferred += bytesTransferred;
}
@Override
public void onTransferEnd(DataSource source, DataSpec dataSpec, boolean isNetwork) {}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment