diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/DefaultExtractorInputTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/DefaultExtractorInputTest.java index dcab0c6275..6abd116086 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/DefaultExtractorInputTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/DefaultExtractorInputTest.java @@ -269,9 +269,8 @@ public class DefaultExtractorInputTest extends TestCase { public void testSkipFullyLarge() throws Exception { // Tests skipping an amount of data that's larger than any internal scratch space. int largeSkipSize = 1024 * 1024; - FakeDataSource.Builder builder = new FakeDataSource.Builder(); - builder.appendReadData(new byte[largeSkipSize]); - FakeDataSource testDataSource = builder.build(); + FakeDataSource testDataSource = new FakeDataSource(); + testDataSource.getDataSet().newDefaultData().appendReadData(new byte[largeSkipSize]); testDataSource.open(new DataSpec(Uri.parse(TEST_URI))); DefaultExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET); @@ -397,29 +396,29 @@ public class DefaultExtractorInputTest extends TestCase { } private static FakeDataSource buildDataSource() throws Exception { - FakeDataSource.Builder builder = new FakeDataSource.Builder(); - builder.appendReadData(Arrays.copyOfRange(TEST_DATA, 0, 3)); - builder.appendReadData(Arrays.copyOfRange(TEST_DATA, 3, 6)); - builder.appendReadData(Arrays.copyOfRange(TEST_DATA, 6, 9)); - FakeDataSource testDataSource = builder.build(); + FakeDataSource testDataSource = new FakeDataSource(); + testDataSource.getDataSet().newDefaultData() + .appendReadData(Arrays.copyOfRange(TEST_DATA, 0, 3)) + .appendReadData(Arrays.copyOfRange(TEST_DATA, 3, 6)) + .appendReadData(Arrays.copyOfRange(TEST_DATA, 6, 9)); testDataSource.open(new DataSpec(Uri.parse(TEST_URI))); return testDataSource; } private static FakeDataSource buildFailingDataSource() throws Exception { - FakeDataSource.Builder builder = new FakeDataSource.Builder(); - builder.appendReadData(Arrays.copyOfRange(TEST_DATA, 0, 6)); - builder.appendReadError(new IOException()); - builder.appendReadData(Arrays.copyOfRange(TEST_DATA, 6, 9)); - FakeDataSource testDataSource = builder.build(); + FakeDataSource testDataSource = new FakeDataSource(); + testDataSource.getDataSet().newDefaultData() + .appendReadData(Arrays.copyOfRange(TEST_DATA, 0, 6)) + .appendReadError(new IOException()) + .appendReadData(Arrays.copyOfRange(TEST_DATA, 6, 9)); testDataSource.open(new DataSpec(Uri.parse(TEST_URI))); return testDataSource; } private static FakeDataSource buildLargeDataSource() throws Exception { - FakeDataSource.Builder builder = new FakeDataSource.Builder(); - builder.appendReadData(new byte[LARGE_TEST_DATA_LENGTH]); - FakeDataSource testDataSource = builder.build(); + FakeDataSource testDataSource = new FakeDataSource(); + testDataSource.getDataSet().newDefaultData() + .appendReadData(new byte[LARGE_TEST_DATA_LENGTH]); testDataSource.open(new DataSpec(Uri.parse(TEST_URI))); return testDataSource; } diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/DataSourceInputStreamTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/DataSourceInputStreamTest.java index 3200e9d6a3..38797ede66 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/DataSourceInputStreamTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/DataSourceInputStreamTest.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.upstream; +import android.net.Uri; import android.test.MoreAsserts; import com.google.android.exoplayer2.testutil.FakeDataSource; import com.google.android.exoplayer2.testutil.TestUtil; @@ -88,12 +89,13 @@ public class DataSourceInputStreamTest extends TestCase { } private static DataSourceInputStream buildTestInputStream() { - FakeDataSource.Builder fakeDataSourceBuilder = new FakeDataSource.Builder() + FakeDataSource fakeDataSource = new FakeDataSource(); + fakeDataSource.getDataSet().newDefaultData() .appendReadData(Arrays.copyOfRange(TEST_DATA, 0, 5)) .appendReadData(Arrays.copyOfRange(TEST_DATA, 5, 10)) .appendReadData(Arrays.copyOfRange(TEST_DATA, 10, 15)) .appendReadData(Arrays.copyOfRange(TEST_DATA, 15, TEST_DATA.length)); - return new DataSourceInputStream(fakeDataSourceBuilder.build(), new DataSpec(null)); + return new DataSourceInputStream(fakeDataSource, new DataSpec(Uri.EMPTY)); } } diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceTest.java index 6689d73ff1..c76e4989d8 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceTest.java @@ -20,6 +20,7 @@ import android.test.InstrumentationTestCase; import android.test.MoreAsserts; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.testutil.FakeDataSource; +import com.google.android.exoplayer2.testutil.FakeDataSource.FakeData; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.FileDataSource; import com.google.android.exoplayer2.util.Util; @@ -197,12 +198,12 @@ public class CacheDataSourceTest extends InstrumentationTestCase { private CacheDataSource createCacheDataSource(boolean setReadException, boolean simulateUnknownLength, @CacheDataSource.Flags int flags, CacheDataSink cacheWriteDataSink) { - FakeDataSource.Builder builder = new FakeDataSource.Builder(); + FakeDataSource upstream = new FakeDataSource(); + FakeData fakeData = upstream.getDataSet().newDefaultData() + .setSimulateUnknownLength(simulateUnknownLength).appendReadData(TEST_DATA); if (setReadException) { - builder.appendReadError(new IOException("Shouldn't read from upstream")); + fakeData.appendReadError(new IOException("Shouldn't read from upstream")); } - FakeDataSource upstream = - builder.setSimulateUnknownLength(simulateUnknownLength).appendReadData(TEST_DATA).build(); return new CacheDataSource(simpleCache, upstream, new FileDataSource(), cacheWriteDataSink, flags, null); } diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceTest2.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceTest2.java index 70a7d797c1..7e8088f3be 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceTest2.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceTest2.java @@ -139,7 +139,9 @@ public class CacheDataSourceTest2 extends AndroidTestCase { } private static FakeDataSource buildFakeUpstreamSource() { - return new FakeDataSource.Builder().appendReadData(DATA).build(); + FakeDataSource fakeDataSource = new FakeDataSource(); + fakeDataSource.getDataSet().newDefaultData().appendReadData(DATA); + return fakeDataSource; } private static CacheDataSource buildCacheDataSource(Context context, DataSource upstreamSource, diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeDataSource.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeDataSource.java index 37947bb1ab..a7b849f233 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeDataSource.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeDataSource.java @@ -23,43 +23,79 @@ import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; /** - * A fake {@link DataSource} capable of simulating various scenarios. - *

- * The data that will be read from the source can be constructed by calling - * {@link Builder#appendReadData(byte[])}. Calls to {@link #read(byte[], int, int)} will not span - * the boundaries between arrays passed to successive calls, and hence the boundaries control the + * A fake {@link DataSource} capable of simulating various scenarios. It uses a {@link FakeDataSet} + * instance which determines the response to data access calls. + * + *

Multiple fake data can be defined by {@link FakeDataSet#setData(String, byte[])} and {@link + * FakeDataSet#newData(String)} methods. It's also possible to define a default data by {@link + * FakeDataSet#newDefaultData()}. + * + *

{@link FakeDataSet#newData(String)} and {@link FakeDataSet#newDefaultData()} return a {@link + * FakeData} instance which can be used to define specific results during {@link #read(byte[], int, + * int)} calls. + * + *

The data that will be read from the source can be constructed by calling {@link + * FakeData#appendReadData(byte[])} Calls to {@link #read(byte[], int, int)} will not span the + * boundaries between arrays passed to successive calls, and hence the boundaries control the * positions at which read requests to the source may only be partially satisfied. - *

- * Errors can be inserted by calling {@link Builder#appendReadError(IOException)}. An inserted error - * will be thrown from the first call to {@link #read(byte[], int, int)} that attempts to read from - * the corresponding position, and from all subsequent calls to {@link #read(byte[], int, int)} + * + *

Errors can be inserted by calling {@link FakeData#appendReadError(IOException)}. An inserted + * error will be thrown from the first call to {@link #read(byte[], int, int)} that attempts to read + * from the corresponding position, and from all subsequent calls to {@link #read(byte[], int, int)} * until the source is closed. If the source is closed and re-opened having encountered an error, * that error will not be thrown again. + * + *

Example usage: + * + *

+ *   // Create a FakeDataSource then add default data and two FakeData
+ *   // "test_file" throws an IOException when tried to be read until closed and reopened.
+ *   FakeDataSource fakeDataSource = new FakeDataSource();
+ *   fakeDataSource.getDataSet()
+ *       .newDefaultData()
+ *         .appendReadData(defaultData)
+ *         .endData()
+ *       .setData("http:///1", data1)
+ *       .newData("test_file")
+ *         .appendReadError(new IOException())
+ *         .appendReadData(data2);
+ *    // No need to call endData at the end
+ * 
*/ public final class FakeDataSource implements DataSource { - private final ArrayList segments; + private final FakeDataSet fakeDataSet; private final ArrayList openedDataSpecs; - private final boolean simulateUnknownLength; - private final long totalLength; - private Uri uri; private boolean opened; + private FakeData fakeData; private int currentSegmentIndex; private long bytesRemaining; - private FakeDataSource(boolean simulateUnknownLength, ArrayList segments) { - this.simulateUnknownLength = simulateUnknownLength; - this.segments = segments; - long totalLength = 0; - for (Segment segment : segments) { - totalLength += segment.length; - } - this.totalLength = totalLength; - openedDataSpecs = new ArrayList<>(); + public static Factory newFactory(final FakeDataSet fakeDataSet) { + return new Factory() { + @Override + public DataSource createDataSource() { + return new FakeDataSource(fakeDataSet); + } + }; + } + + public FakeDataSource() { + this(new FakeDataSet()); + } + + public FakeDataSource(FakeDataSet fakeDataSet) { + this.fakeDataSet = fakeDataSet; + this.openedDataSpecs = new ArrayList<>(); + } + + public FakeDataSet getDataSet() { + return fakeDataSet; } @Override @@ -69,6 +105,21 @@ public final class FakeDataSource implements DataSource { opened = true; uri = dataSpec.uri; openedDataSpecs.add(dataSpec); + + fakeData = fakeDataSet.getData(uri.toString()); + if (fakeData == null) { + throw new IOException("Data not found: " + dataSpec.uri); + } + + long totalLength = 0; + for (Segment segment : fakeData.segments) { + totalLength += segment.length; + } + + if (totalLength == 0) { + throw new IOException("Data is empty: " + dataSpec.uri); + } + // If the source knows that the request is unsatisfiable then fail. if (dataSpec.position >= totalLength || (dataSpec.length != C.LENGTH_UNSET && (dataSpec.position + dataSpec.length > totalLength))) { @@ -78,7 +129,7 @@ public final class FakeDataSource implements DataSource { boolean findingCurrentSegmentIndex = true; currentSegmentIndex = 0; int scannedLength = 0; - for (Segment segment : segments) { + for (Segment segment : fakeData.segments) { segment.bytesRead = (int) Math.min(Math.max(0, dataSpec.position - scannedLength), segment.length); scannedLength += segment.length; @@ -91,7 +142,7 @@ public final class FakeDataSource implements DataSource { // Configure bytesRemaining, and return. if (dataSpec.length == C.LENGTH_UNSET) { bytesRemaining = totalLength - dataSpec.position; - return simulateUnknownLength ? C.LENGTH_UNSET : bytesRemaining; + return fakeData.simulateUnknownLength ? C.LENGTH_UNSET : bytesRemaining; } else { bytesRemaining = dataSpec.length; return bytesRemaining; @@ -102,10 +153,10 @@ public final class FakeDataSource implements DataSource { public int read(byte[] buffer, int offset, int readLength) throws IOException { Assertions.checkState(opened); while (true) { - if (currentSegmentIndex == segments.size() || bytesRemaining == 0) { + if (currentSegmentIndex == fakeData.segments.size() || bytesRemaining == 0) { return C.RESULT_END_OF_INPUT; } - Segment current = segments.get(currentSegmentIndex); + Segment current = fakeData.segments.get(currentSegmentIndex); if (current.isErrorSegment()) { if (!current.exceptionCleared) { current.exceptionThrown = true; @@ -140,12 +191,13 @@ public final class FakeDataSource implements DataSource { Assertions.checkState(opened); opened = false; uri = null; - if (currentSegmentIndex < segments.size()) { - Segment current = segments.get(currentSegmentIndex); + if (currentSegmentIndex < fakeData.segments.size()) { + Segment current = fakeData.segments.get(currentSegmentIndex); if (current.isErrorSegment() && current.exceptionThrown) { current.exceptionCleared = true; } } + fakeData = null; } /** @@ -181,16 +233,21 @@ public final class FakeDataSource implements DataSource { } - /** - * Builder of {@link FakeDataSource} instances. - */ - public static final class Builder { + /** Container of fake data to be served by a {@link FakeDataSource}. */ + public static final class FakeData { private final ArrayList segments; + private final FakeDataSet dataSet; private boolean simulateUnknownLength; - public Builder() { - segments = new ArrayList<>(); + public FakeData(FakeDataSet dataSet) { + this.segments = new ArrayList<>(); + this.dataSet = dataSet; + } + + /** Returns the {@link FakeDataSet} this FakeData belongs to. */ + public FakeDataSet endData() { + return dataSet; } /** @@ -199,7 +256,7 @@ public final class FakeDataSource implements DataSource { * the {@link DataSpec#length} of the argument, including the case where the length is equal to * {@link C#LENGTH_UNSET}. */ - public Builder setSimulateUnknownLength(boolean simulateUnknownLength) { + public FakeData setSimulateUnknownLength(boolean simulateUnknownLength) { this.simulateUnknownLength = simulateUnknownLength; return this; } @@ -207,7 +264,7 @@ public final class FakeDataSource implements DataSource { /** * Appends to the underlying data. */ - public Builder appendReadData(byte[] data) { + public FakeData appendReadData(byte[] data) { Assertions.checkState(data != null && data.length > 0); segments.add(new Segment(data, null)); return this; @@ -216,13 +273,41 @@ public final class FakeDataSource implements DataSource { /** * Appends an error in the underlying data. */ - public Builder appendReadError(IOException exception) { + public FakeData appendReadError(IOException exception) { segments.add(new Segment(null, exception)); return this; } + } - public FakeDataSource build() { - return new FakeDataSource(simulateUnknownLength, segments); + /** A set of {@link FakeData} instances. */ + public static final class FakeDataSet { + + private FakeData defaultData; + private final HashMap dataMap; + + public FakeDataSet() { + dataMap = new HashMap<>(); + } + + public FakeData newDefaultData() { + defaultData = new FakeData(this); + return defaultData; + } + + public FakeData newData(String uri) { + FakeData data = new FakeData(this); + dataMap.put(uri, data); + return data; + } + + public FakeDataSet setData(String uri, byte[] data) { + newData(uri).appendReadData(data); + return this; + } + + public FakeData getData(String uri) { + FakeData data = dataMap.get(uri); + return data != null ? data : defaultData; } }