Make FakeDataSource able to serve multiple fake files
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=150748155
This commit is contained in:
parent
c32533cad8
commit
065d3dc523
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>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()}.
|
||||
*
|
||||
* <p>{@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.
|
||||
*
|
||||
* <p>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.
|
||||
* <p>
|
||||
* 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)}
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>Example usage:
|
||||
*
|
||||
* <pre>
|
||||
* // 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
|
||||
* </pre>
|
||||
*/
|
||||
public final class FakeDataSource implements DataSource {
|
||||
|
||||
private final ArrayList<Segment> segments;
|
||||
private final FakeDataSet fakeDataSet;
|
||||
private final ArrayList<DataSpec> 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<Segment> 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<Segment> 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<String, FakeData> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user