Allow to extend FakeDataSource.

Extensions can perform additional actions whenever data is read (e.g. sleeping
to simulate bandwidth restrictions).

Additionally, the factory class can also be overwritten and allows to set the
FakeDataSet later in case it is not available right away.

Moreover, this class now also uses a transfer listener similar to all real data sources.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=162326000
This commit is contained in:
tonihei 2017-07-18 01:47:59 -07:00 committed by Oliver Woodman
parent c28a2a4100
commit f77e96bca8
2 changed files with 61 additions and 20 deletions

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
@ -77,11 +78,11 @@ public class FakeDataSet {
*/ */
public static final class Segment { public static final class Segment {
public final IOException exception; public @Nullable final IOException exception;
public final byte[] data; public @Nullable final byte[] data;
public final int length; public final int length;
public final long byteOffset; public final long byteOffset;
public final Runnable action; public @Nullable final Runnable action;
public boolean exceptionThrown; public boolean exceptionThrown;
public boolean exceptionCleared; public boolean exceptionCleared;

View File

@ -16,12 +16,14 @@
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData; import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData;
import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData.Segment; import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData.Segment;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSourceException; import com.google.android.exoplayer2.upstream.DataSourceException;
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 java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -30,9 +32,34 @@ import java.util.ArrayList;
* A fake {@link DataSource} capable of simulating various scenarios. It uses a {@link FakeDataSet} * A fake {@link DataSource} capable of simulating various scenarios. It uses a {@link FakeDataSet}
* instance which determines the response to data access calls. * instance which determines the response to data access calls.
*/ */
public final class FakeDataSource implements DataSource { public class FakeDataSource implements DataSource {
/**
* Factory to create a {@link FakeDataSource}.
*/
public static class Factory implements DataSource.Factory {
protected final TransferListener<? super FakeDataSource> transferListener;
protected FakeDataSet fakeDataSet;
public Factory(@Nullable TransferListener<? super FakeDataSource> transferListener) {
this.transferListener = transferListener;
}
public final Factory setFakeDataSet(FakeDataSet fakeDataSet) {
this.fakeDataSet = fakeDataSet;
return this;
}
@Override
public DataSource createDataSource() {
return new FakeDataSource(fakeDataSet, transferListener);
}
}
private final FakeDataSet fakeDataSet; private final FakeDataSet fakeDataSet;
private final TransferListener<? super FakeDataSource> transferListener;
private final ArrayList<DataSpec> openedDataSpecs; private final ArrayList<DataSpec> openedDataSpecs;
private Uri uri; private Uri uri;
@ -41,30 +68,28 @@ public final class FakeDataSource implements DataSource {
private int currentSegmentIndex; private int currentSegmentIndex;
private long bytesRemaining; private long bytesRemaining;
public static Factory newFactory(final FakeDataSet fakeDataSet) {
return new Factory() {
@Override
public DataSource createDataSource() {
return new FakeDataSource(fakeDataSet);
}
};
}
public FakeDataSource() { public FakeDataSource() {
this(new FakeDataSet()); this(new FakeDataSet());
} }
public FakeDataSource(FakeDataSet fakeDataSet) { public FakeDataSource(FakeDataSet fakeDataSet) {
this(fakeDataSet, null);
}
public FakeDataSource(FakeDataSet fakeDataSet,
@Nullable TransferListener<? super FakeDataSource> transferListener) {
Assertions.checkNotNull(fakeDataSet);
this.fakeDataSet = fakeDataSet; this.fakeDataSet = fakeDataSet;
this.transferListener = transferListener;
this.openedDataSpecs = new ArrayList<>(); this.openedDataSpecs = new ArrayList<>();
} }
public FakeDataSet getDataSet() { public final FakeDataSet getDataSet() {
return fakeDataSet; return fakeDataSet;
} }
@Override @Override
public long open(DataSpec dataSpec) throws IOException { public final long open(DataSpec dataSpec) throws IOException {
Assertions.checkState(!opened); Assertions.checkState(!opened);
// DataSpec requires a matching close call even if open fails. // DataSpec requires a matching close call even if open fails.
opened = true; opened = true;
@ -104,6 +129,9 @@ public final class FakeDataSource implements DataSource {
currentSegmentIndex++; currentSegmentIndex++;
} }
} }
if (transferListener != null) {
transferListener.onTransferStart(this, dataSpec);
}
// Configure bytesRemaining, and return. // Configure bytesRemaining, and return.
if (dataSpec.length == C.LENGTH_UNSET) { if (dataSpec.length == C.LENGTH_UNSET) {
bytesRemaining = totalLength - dataSpec.position; bytesRemaining = totalLength - dataSpec.position;
@ -115,7 +143,7 @@ public final class FakeDataSource implements DataSource {
} }
@Override @Override
public int read(byte[] buffer, int offset, int readLength) throws IOException { public final int read(byte[] buffer, int offset, int readLength) throws IOException {
Assertions.checkState(opened); Assertions.checkState(opened);
while (true) { while (true) {
if (currentSegmentIndex == fakeData.getSegments().size() || bytesRemaining == 0) { if (currentSegmentIndex == fakeData.getSegments().size() || bytesRemaining == 0) {
@ -138,7 +166,13 @@ public final class FakeDataSource implements DataSource {
// Do not allow crossing of the segment boundary. // Do not allow crossing of the segment boundary.
readLength = Math.min(readLength, current.length - current.bytesRead); readLength = Math.min(readLength, current.length - current.bytesRead);
// Perform the read and return. // Perform the read and return.
if (current.data != null) {
System.arraycopy(current.data, current.bytesRead, buffer, offset, readLength); System.arraycopy(current.data, current.bytesRead, buffer, offset, readLength);
}
onDataRead(readLength);
if (transferListener != null) {
transferListener.onBytesTransferred(this, readLength);
}
bytesRemaining -= readLength; bytesRemaining -= readLength;
current.bytesRead += readLength; current.bytesRead += readLength;
if (current.bytesRead == current.length) { if (current.bytesRead == current.length) {
@ -150,12 +184,12 @@ public final class FakeDataSource implements DataSource {
} }
@Override @Override
public Uri getUri() { public final Uri getUri() {
return uri; return uri;
} }
@Override @Override
public void close() throws IOException { public final void close() throws IOException {
Assertions.checkState(opened); Assertions.checkState(opened);
opened = false; opened = false;
uri = null; uri = null;
@ -165,6 +199,9 @@ public final class FakeDataSource implements DataSource {
current.exceptionCleared = true; current.exceptionCleared = true;
} }
} }
if (transferListener != null) {
transferListener.onTransferEnd(this);
}
fakeData = null; fakeData = null;
} }
@ -172,12 +209,15 @@ public final class FakeDataSource implements DataSource {
* Returns the {@link DataSpec} instances passed to {@link #open(DataSpec)} since the last call to * Returns the {@link DataSpec} instances passed to {@link #open(DataSpec)} since the last call to
* this method. * this method.
*/ */
public DataSpec[] getAndClearOpenedDataSpecs() { public final DataSpec[] getAndClearOpenedDataSpecs() {
DataSpec[] dataSpecs = new DataSpec[openedDataSpecs.size()]; DataSpec[] dataSpecs = new DataSpec[openedDataSpecs.size()];
openedDataSpecs.toArray(dataSpecs); openedDataSpecs.toArray(dataSpecs);
openedDataSpecs.clear(); openedDataSpecs.clear();
return dataSpecs; return dataSpecs;
} }
protected void onDataRead(int bytesRead) {
// Do nothing. Can be overridden.
}
} }