Start fixing nullness in testutil

PiperOrigin-RevId: 292340530
This commit is contained in:
olly 2020-01-30 15:05:32 +00:00 committed by Oliver Woodman
parent ff822ff9fd
commit 7c8a54c6d0
23 changed files with 175 additions and 112 deletions

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source.chunk; package com.google.android.exoplayer2.source.chunk;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput; import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
@ -54,7 +55,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
DataSpec dataSpec, DataSpec dataSpec,
Format trackFormat, Format trackFormat,
int trackSelectionReason, int trackSelectionReason,
Object trackSelectionData, @Nullable Object trackSelectionData,
long startTimeUs, long startTimeUs,
long endTimeUs, long endTimeUs,
long chunkIndex, long chunkIndex,

View File

@ -821,7 +821,8 @@ public final class ExoPlayerTest {
new FakeMediaSource(timeline, Builder.VIDEO_FORMAT, Builder.AUDIO_FORMAT); new FakeMediaSource(timeline, Builder.VIDEO_FORMAT, Builder.AUDIO_FORMAT);
FakeRenderer videoRenderer = new FakeRenderer(Builder.VIDEO_FORMAT); FakeRenderer videoRenderer = new FakeRenderer(Builder.VIDEO_FORMAT);
FakeRenderer audioRenderer = new FakeRenderer(Builder.AUDIO_FORMAT); FakeRenderer audioRenderer = new FakeRenderer(Builder.AUDIO_FORMAT);
final FakeTrackSelector trackSelector = new FakeTrackSelector(/* reuse track selection */ true); final FakeTrackSelector trackSelector =
new FakeTrackSelector(/* mayReuseTrackSelection= */ true);
ActionSchedule disableTrackAction = ActionSchedule disableTrackAction =
new ActionSchedule.Builder("testReuseTrackSelection") new ActionSchedule.Builder("testReuseTrackSelection")
.pause() .pause()

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import com.google.android.exoplayer2.util.HandlerWrapper; import com.google.android.exoplayer2.util.HandlerWrapper;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* {@link FakeClock} extension which automatically advances time whenever an empty message is * {@link FakeClock} extension which automatically advances time whenever an empty message is
@ -25,7 +26,7 @@ import com.google.android.exoplayer2.util.HandlerWrapper;
*/ */
public final class AutoAdvancingFakeClock extends FakeClock { public final class AutoAdvancingFakeClock extends FakeClock {
private HandlerWrapper autoAdvancingHandler; private @MonotonicNonNull HandlerWrapper autoAdvancingHandler;
public AutoAdvancingFakeClock() { public AutoAdvancingFakeClock() {
super(/* initialTimeMs= */ 0); super(/* initialTimeMs= */ 0);

View File

@ -21,6 +21,7 @@ import android.content.Context;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.audio.AudioSink; import com.google.android.exoplayer2.audio.AudioSink;
import com.google.android.exoplayer2.audio.ForwardingAudioSink; import com.google.android.exoplayer2.audio.ForwardingAudioSink;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -122,7 +123,7 @@ public final class CapturingAudioSink extends ForwardingAudioSink implements Dum
if (WRITE_DUMP) { if (WRITE_DUMP) {
File directory = context.getExternalFilesDir(null); File directory = context.getExternalFilesDir(null);
File file = new File(directory, dumpFile); File file = new File(directory, dumpFile);
file.getParentFile().mkdirs(); Assertions.checkStateNotNull(file.getParentFile()).mkdirs();
PrintWriter out = new PrintWriter(file); PrintWriter out = new PrintWriter(file);
out.print(actual); out.print(actual);
out.close(); out.close();

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
@ -44,7 +45,7 @@ public final class Dumper {
sb = new StringBuilder(); sb = new StringBuilder();
} }
public Dumper add(String field, Object value) { public Dumper add(String field, @Nullable Object value) {
return addString(field + " = " + value + '\n'); return addString(field + " = " + value + '\n');
} }
@ -53,9 +54,14 @@ public final class Dumper {
return this; return this;
} }
public Dumper add(String field, byte[] value) { public Dumper add(String field, @Nullable byte[] value) {
String string = String.format(Locale.US, "%s = length %d, hash %X\n", field, value.length, String string =
Arrays.hashCode(value)); String.format(
Locale.US,
"%s = length %d, hash %X\n",
field,
value == null ? 0 : value.length,
Arrays.hashCode(value));
return addString(string); return addString(string);
} }

View File

@ -209,7 +209,7 @@ public final class ExtractorAsserts {
} }
// If the SeekMap is seekable, test seeking in the stream. // If the SeekMap is seekable, test seeking in the stream.
SeekMap seekMap = extractorOutput.seekMap; SeekMap seekMap = Assertions.checkNotNull(extractorOutput.seekMap);
if (seekMap.isSeekable()) { if (seekMap.isSeekable()) {
long durationUs = seekMap.getDurationUs(); long durationUs = seekMap.getDurationUs();
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
@ -361,7 +361,7 @@ public final class ExtractorAsserts {
int i = fileName.lastIndexOf('/'); int i = fileName.lastIndexOf('/');
String path = i >= 0 ? fileName.substring(0, i) : ""; String path = i >= 0 ? fileName.substring(0, i) : "";
String file = i >= 0 ? fileName.substring(i + 1) : fileName; String file = i >= 0 ? fileName.substring(i + 1) : fileName;
return Arrays.asList(context.getResources().getAssets().list(path)).contains(file); String[] assets = context.getResources().getAssets().list(path);
return assets != null && Arrays.asList(assets).contains(file);
} }
} }

View File

@ -23,6 +23,7 @@ import com.google.android.exoplayer2.source.chunk.BaseMediaChunkIterator;
import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; import com.google.android.exoplayer2.source.chunk.MediaChunkIterator;
import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData.Segment; import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData.Segment;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Util;
import java.util.Random; import java.util.Random;
/** /**
@ -92,7 +93,7 @@ public final class FakeAdaptiveDataSet extends FakeDataSet {
checkInBounds(); checkInBounds();
String uri = dataSet.getUri(trackGroupIndex); String uri = dataSet.getUri(trackGroupIndex);
int chunkIndex = (int) getCurrentIndex(); int chunkIndex = (int) getCurrentIndex();
Segment fakeDataChunk = dataSet.getData(uri).getSegments().get(chunkIndex); Segment fakeDataChunk = Util.castNonNull(dataSet.getData(uri)).getSegments().get(chunkIndex);
return new DataSpec( return new DataSpec(
Uri.parse(uri), fakeDataChunk.byteOffset, fakeDataChunk.length, /* key= */ null); Uri.parse(uri), fakeDataChunk.byteOffset, fakeDataChunk.length, /* key= */ null);
} }

View File

@ -28,9 +28,12 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* Fake {@link MediaPeriod} that provides tracks from the given {@link TrackGroupArray}. Selecting a * Fake {@link MediaPeriod} that provides tracks from the given {@link TrackGroupArray}. Selecting a
@ -44,7 +47,7 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
@Nullable private final TransferListener transferListener; @Nullable private final TransferListener transferListener;
private final long durationUs; private final long durationUs;
private Callback callback; @MonotonicNonNull private Callback callback;
private ChunkSampleStream<FakeChunkSource>[] sampleStreams; private ChunkSampleStream<FakeChunkSource>[] sampleStreams;
private SequenceableLoader sequenceableLoader; private SequenceableLoader sequenceableLoader;
@ -81,9 +84,9 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public long selectTracks( public long selectTracks(
TrackSelection[] selections, @NullableType TrackSelection[] selections,
boolean[] mayRetainStreamFlags, boolean[] mayRetainStreamFlags,
SampleStream[] streams, @NullableType SampleStream[] streams,
boolean[] streamResetFlags, boolean[] streamResetFlags,
long positionUs) { long positionUs) {
long returnPositionUs = super.selectTracks(selections, mayRetainStreamFlags, streams, long returnPositionUs = super.selectTracks(selections, mayRetainStreamFlags, streams,
@ -94,7 +97,8 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
validStreams.add((ChunkSampleStream<FakeChunkSource>) stream); validStreams.add((ChunkSampleStream<FakeChunkSource>) stream);
} }
} }
this.sampleStreams = validStreams.toArray(newSampleStreamArray(validStreams.size())); sampleStreams = newSampleStreamArray(validStreams.size());
validStreams.toArray(sampleStreams);
this.sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); this.sequenceableLoader = new CompositeSequenceableLoader(sampleStreams);
return returnPositionUs; return returnPositionUs;
} }
@ -162,7 +166,7 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
@Override @Override
public void onContinueLoadingRequested(ChunkSampleStream<FakeChunkSource> source) { public void onContinueLoadingRequested(ChunkSampleStream<FakeChunkSource> source) {
callback.onContinueLoadingRequested(this); Assertions.checkStateNotNull(callback).onContinueLoadingRequested(this);
} }
// We won't assign the array to a variable that erases the generic type, and then write into it. // We won't assign the array to a variable that erases the generic type, and then write into it.

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer2.testutil;
import android.os.Handler.Callback; import android.os.Handler.Callback;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.HandlerWrapper; import com.google.android.exoplayer2.util.HandlerWrapper;
import java.util.ArrayList; import java.util.ArrayList;
@ -90,7 +91,7 @@ public class FakeClock implements Clock {
} }
@Override @Override
public HandlerWrapper createHandler(Looper looper, Callback callback) { public HandlerWrapper createHandler(Looper looper, @Nullable Callback callback) {
return new ClockHandler(looper, callback); return new ClockHandler(looper, callback);
} }
@ -119,7 +120,7 @@ public class FakeClock implements Clock {
private final long postTime; private final long postTime;
private final HandlerWrapper handler; private final HandlerWrapper handler;
private final Runnable runnable; @Nullable private final Runnable runnable;
private final int message; private final int message;
public HandlerMessageData(long postTime, HandlerWrapper handler, Runnable runnable) { public HandlerMessageData(long postTime, HandlerWrapper handler, Runnable runnable) {
@ -155,7 +156,7 @@ public class FakeClock implements Clock {
private final android.os.Handler handler; private final android.os.Handler handler;
public ClockHandler(Looper looper, Callback callback) { public ClockHandler(Looper looper, @Nullable Callback callback) {
handler = new android.os.Handler(looper, callback); handler = new android.os.Handler(looper, callback);
} }
@ -170,7 +171,7 @@ public class FakeClock implements Clock {
} }
@Override @Override
public Message obtainMessage(int what, Object obj) { public Message obtainMessage(int what, @Nullable Object obj) {
return handler.obtainMessage(what, obj); return handler.obtainMessage(what, obj);
} }
@ -180,7 +181,7 @@ public class FakeClock implements Clock {
} }
@Override @Override
public Message obtainMessage(int what, int arg1, int arg2, Object obj) { public Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj) {
return handler.obtainMessage(what, arg1, arg2, obj); return handler.obtainMessage(what, arg1, arg2, obj);
} }
@ -200,7 +201,7 @@ public class FakeClock implements Clock {
} }
@Override @Override
public void removeCallbacksAndMessages(Object token) { public void removeCallbacksAndMessages(@Nullable Object token) {
handler.removeCallbacksAndMessages(token); handler.removeCallbacksAndMessages(token);
} }

View File

@ -89,24 +89,28 @@ public class FakeDataSet {
public boolean exceptionCleared; public boolean exceptionCleared;
public int bytesRead; public int bytesRead;
private Segment(byte[] data, Segment previousSegment) { private Segment(byte[] data, @Nullable Segment previousSegment) {
this(data, data.length, null, null, previousSegment); this(data, data.length, null, null, previousSegment);
} }
private Segment(int length, Segment previousSegment) { private Segment(int length, @Nullable Segment previousSegment) {
this(null, length, null, null, previousSegment); this(null, length, null, null, previousSegment);
} }
private Segment(IOException exception, Segment previousSegment) { private Segment(IOException exception, @Nullable Segment previousSegment) {
this(null, 0, exception, null, previousSegment); this(null, 0, exception, null, previousSegment);
} }
private Segment(Runnable action, Segment previousSegment) { private Segment(Runnable action, @Nullable Segment previousSegment) {
this(null, 0, null, action, previousSegment); this(null, 0, null, action, previousSegment);
} }
private Segment(@Nullable byte[] data, int length, @Nullable IOException exception, private Segment(
@Nullable Runnable action, Segment previousSegment) { @Nullable byte[] data,
int length,
@Nullable IOException exception,
@Nullable Runnable action,
@Nullable Segment previousSegment) {
this.exception = exception; this.exception = exception;
this.action = action; this.action = action;
this.data = data; this.data = data;
@ -125,16 +129,17 @@ public class FakeDataSet {
} }
/** Uri of the data or null if this is the default FakeData. */
public final Uri uri;
private final ArrayList<Segment> segments;
private final FakeDataSet dataSet; private final FakeDataSet dataSet;
/** Uri of the data or null if this is the default FakeData. */
@Nullable public final Uri uri;
private final ArrayList<Segment> segments;
private boolean simulateUnknownLength; private boolean simulateUnknownLength;
private FakeData(FakeDataSet dataSet, Uri uri) { private FakeData(FakeDataSet dataSet, @Nullable Uri uri) {
this.dataSet = dataSet;
this.uri = uri; this.uri = uri;
this.segments = new ArrayList<>(); this.segments = new ArrayList<>();
this.dataSet = dataSet;
} }
/** Returns the {@link FakeDataSet} this FakeData belongs to. */ /** Returns the {@link FakeDataSet} this FakeData belongs to. */
@ -157,7 +162,7 @@ public class FakeDataSet {
* Appends to the underlying data. * Appends to the underlying data.
*/ */
public FakeData appendReadData(byte[] data) { public FakeData appendReadData(byte[] data) {
Assertions.checkState(data != null && data.length > 0); Assertions.checkState(data.length > 0);
segments.add(new Segment(data, getLastSegment())); segments.add(new Segment(data, getLastSegment()));
return this; return this;
} }
@ -213,6 +218,7 @@ public class FakeDataSet {
return simulateUnknownLength; return simulateUnknownLength;
} }
@Nullable
private Segment getLastSegment() { private Segment getLastSegment() {
int count = segments.size(); int count = segments.size();
return count > 0 ? segments.get(count - 1) : null; return count > 0 ? segments.get(count - 1) : null;
@ -221,7 +227,7 @@ public class FakeDataSet {
} }
private final HashMap<Uri, FakeData> dataMap; private final HashMap<Uri, FakeData> dataMap;
private FakeData defaultData; @Nullable private FakeData defaultData;
public FakeDataSet() { public FakeDataSet() {
dataMap = new HashMap<>(); dataMap = new HashMap<>();
@ -266,13 +272,15 @@ public class FakeDataSet {
} }
/** Returns the data for the given {@code uri}, or {@code defaultData} if no data is set. */ /** Returns the data for the given {@code uri}, or {@code defaultData} if no data is set. */
@Nullable
public FakeData getData(String uri) { public FakeData getData(String uri) {
return getData(Uri.parse(uri)); return getData(Uri.parse(uri));
} }
/** Returns the data for the given {@code uri}, or {@code defaultData} if no data is set. */ /** Returns the data for the given {@code uri}, or {@code defaultData} if no data is set. */
@Nullable
public FakeData getData(Uri uri) { public FakeData getData(Uri uri) {
FakeData data = dataMap.get(uri); @Nullable FakeData data = dataMap.get(uri);
return data != null ? data : defaultData; return data != null ? data : defaultData;
} }

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import android.net.Uri; import android.net.Uri;
import androidx.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;
@ -24,8 +25,10 @@ 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.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* 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}
@ -38,7 +41,7 @@ public class FakeDataSource extends BaseDataSource {
*/ */
public static class Factory implements DataSource.Factory { public static class Factory implements DataSource.Factory {
protected FakeDataSet fakeDataSet; protected @MonotonicNonNull FakeDataSet fakeDataSet;
protected boolean isNetwork; protected boolean isNetwork;
public final Factory setFakeDataSet(FakeDataSet fakeDataSet) { public final Factory setFakeDataSet(FakeDataSet fakeDataSet) {
@ -53,17 +56,17 @@ public class FakeDataSource extends BaseDataSource {
@Override @Override
public FakeDataSource createDataSource() { public FakeDataSource createDataSource() {
return new FakeDataSource(fakeDataSet, isNetwork); return new FakeDataSource(Assertions.checkStateNotNull(fakeDataSet), isNetwork);
} }
} }
private final FakeDataSet fakeDataSet; private final FakeDataSet fakeDataSet;
private final ArrayList<DataSpec> openedDataSpecs; private final ArrayList<DataSpec> openedDataSpecs;
private Uri uri; @Nullable private Uri uri;
private boolean openCalled; private boolean openCalled;
private boolean sourceOpened; private boolean sourceOpened;
private FakeData fakeData; @Nullable private FakeData fakeData;
private int currentSegmentIndex; private int currentSegmentIndex;
private long bytesRemaining; private long bytesRemaining;
@ -96,10 +99,11 @@ public class FakeDataSource extends BaseDataSource {
openedDataSpecs.add(dataSpec); openedDataSpecs.add(dataSpec);
transferInitializing(dataSpec); transferInitializing(dataSpec);
fakeData = fakeDataSet.getData(uri.toString()); FakeData fakeData = fakeDataSet.getData(dataSpec.uri.toString());
if (fakeData == null) { if (fakeData == null) {
throw new IOException("Data not found: " + dataSpec.uri); throw new IOException("Data not found: " + dataSpec.uri);
} }
this.fakeData = fakeData;
long totalLength = 0; long totalLength = 0;
for (Segment segment : fakeData.getSegments()) { for (Segment segment : fakeData.getSegments()) {
@ -145,6 +149,7 @@ public class FakeDataSource extends BaseDataSource {
public final int read(byte[] buffer, int offset, int readLength) throws IOException { public final int read(byte[] buffer, int offset, int readLength) throws IOException {
Assertions.checkState(sourceOpened); Assertions.checkState(sourceOpened);
while (true) { while (true) {
FakeData fakeData = Util.castNonNull(this.fakeData);
if (currentSegmentIndex == fakeData.getSegments().size() || bytesRemaining == 0) { if (currentSegmentIndex == fakeData.getSegments().size() || bytesRemaining == 0) {
return C.RESULT_END_OF_INPUT; return C.RESULT_END_OF_INPUT;
} }
@ -152,13 +157,13 @@ public class FakeDataSource extends BaseDataSource {
if (current.isErrorSegment()) { if (current.isErrorSegment()) {
if (!current.exceptionCleared) { if (!current.exceptionCleared) {
current.exceptionThrown = true; current.exceptionThrown = true;
throw (IOException) current.exception.fillInStackTrace(); throw (IOException) Util.castNonNull(current.exception).fillInStackTrace();
} else { } else {
currentSegmentIndex++; currentSegmentIndex++;
} }
} else if (current.isActionSegment()) { } else if (current.isActionSegment()) {
currentSegmentIndex++; currentSegmentIndex++;
current.action.run(); Util.castNonNull(current.action).run();
} else { } else {
// Read at most bytesRemaining. // Read at most bytesRemaining.
readLength = (int) Math.min(readLength, bytesRemaining); readLength = (int) Math.min(readLength, bytesRemaining);
@ -181,13 +186,14 @@ public class FakeDataSource extends BaseDataSource {
} }
} }
@Nullable
@Override @Override
public final Uri getUri() { public final Uri getUri() {
return uri; return uri;
} }
@Override @Override
public final void close() throws IOException { public final void close() {
Assertions.checkState(openCalled); Assertions.checkState(openCalled);
openCalled = false; openCalled = false;
uri = null; uri = null;

View File

@ -20,12 +20,15 @@ import static com.google.common.truth.Truth.assertWithMessage;
import android.content.Context; import android.content.Context;
import android.util.SparseArray; import android.util.SparseArray;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.util.Assertions;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* A fake {@link ExtractorOutput}. * A fake {@link ExtractorOutput}.
@ -45,7 +48,7 @@ public final class FakeExtractorOutput implements ExtractorOutput, Dumper.Dumpab
public int numberOfTracks; public int numberOfTracks;
public boolean tracksEnded; public boolean tracksEnded;
public SeekMap seekMap; public @MonotonicNonNull SeekMap seekMap;
public FakeExtractorOutput() { public FakeExtractorOutput() {
trackOutputs = new SparseArray<>(); trackOutputs = new SparseArray<>();
@ -53,7 +56,7 @@ public final class FakeExtractorOutput implements ExtractorOutput, Dumper.Dumpab
@Override @Override
public FakeTrackOutput track(int id, int type) { public FakeTrackOutput track(int id, int type) {
FakeTrackOutput output = trackOutputs.get(id); @Nullable FakeTrackOutput output = trackOutputs.get(id);
if (output == null) { if (output == null) {
assertThat(tracksEnded).isFalse(); assertThat(tracksEnded).isFalse();
numberOfTracks++; numberOfTracks++;
@ -99,10 +102,11 @@ public final class FakeExtractorOutput implements ExtractorOutput, Dumper.Dumpab
assertThat(seekMap).isNull(); assertThat(seekMap).isNull();
} else { } else {
// TODO: Bulk up this check if possible. // TODO: Bulk up this check if possible.
assertThat(seekMap).isNotNull(); SeekMap expectedSeekMap = Assertions.checkNotNull(expected.seekMap);
assertThat(seekMap.getClass()).isEqualTo(expected.seekMap.getClass()); SeekMap seekMap = Assertions.checkNotNull(this.seekMap);
assertThat(seekMap.isSeekable()).isEqualTo(expected.seekMap.isSeekable()); assertThat(seekMap.getClass()).isEqualTo(expectedSeekMap.getClass());
assertThat(seekMap.getSeekPoints(0)).isEqualTo(expected.seekMap.getSeekPoints(0)); assertThat(seekMap.isSeekable()).isEqualTo(expectedSeekMap.isSeekable());
assertThat(seekMap.getSeekPoints(0)).isEqualTo(expectedSeekMap.getSeekPoints(0));
} }
for (int i = 0; i < numberOfTracks; i++) { for (int i = 0; i < numberOfTracks; i++) {
assertThat(trackOutputs.keyAt(i)).isEqualTo(expected.trackOutputs.keyAt(i)); assertThat(trackOutputs.keyAt(i)).isEqualTo(expected.trackOutputs.keyAt(i));
@ -125,7 +129,7 @@ public final class FakeExtractorOutput implements ExtractorOutput, Dumper.Dumpab
if (WRITE_DUMP) { if (WRITE_DUMP) {
File file = new File(System.getProperty("user.dir"), "src/test/assets"); File file = new File(System.getProperty("user.dir"), "src/test/assets");
file = new File(file, dumpFile); file = new File(file, dumpFile);
file.getParentFile().mkdirs(); Assertions.checkStateNotNull(file.getParentFile()).mkdirs();
PrintWriter out = new PrintWriter(file); PrintWriter out = new PrintWriter(file);
out.print(actual); out.print(actual);
out.close(); out.close();

View File

@ -22,7 +22,6 @@ import com.google.android.exoplayer2.source.chunk.MediaChunk;
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.DefaultHttpDataSource; import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import java.io.IOException;
/** Fake {@link MediaChunk}. */ /** Fake {@link MediaChunk}. */
public final class FakeMediaChunk extends MediaChunk { public final class FakeMediaChunk extends MediaChunk {
@ -51,7 +50,7 @@ public final class FakeMediaChunk extends MediaChunk {
} }
@Override @Override
public void load() throws IOException, InterruptedException { public void load() {
// Do nothing. // Do nothing.
} }

View File

@ -35,6 +35,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** /**
* Fake {@link MediaPeriod} that provides tracks from the given {@link TrackGroupArray}. Selecting * Fake {@link MediaPeriod} that provides tracks from the given {@link TrackGroupArray}. Selecting
@ -149,8 +150,12 @@ public class FakeMediaPeriod implements MediaPeriod {
} }
@Override @Override
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags, public long selectTracks(
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) { @NullableType TrackSelection[] selections,
boolean[] mayRetainStreamFlags,
@NullableType SampleStream[] streams,
boolean[] streamResetFlags,
long positionUs) {
assertThat(prepared).isTrue(); assertThat(prepared).isTrue();
sampleStreams.clear(); sampleStreams.clear();
int rendererCount = selections.length; int rendererCount = selections.length;

View File

@ -84,7 +84,7 @@ public class FakeRenderer extends BaseRenderer {
if (result == C.RESULT_FORMAT_READ) { if (result == C.RESULT_FORMAT_READ) {
formatReadCount++; formatReadCount++;
assertThat(expectedFormats).contains(formatHolder.format); assertThat(expectedFormats).contains(formatHolder.format);
onFormatChanged(formatHolder.format); onFormatChanged(Assertions.checkNotNull(formatHolder.format));
} else if (result == C.RESULT_BUFFER_READ) { } else if (result == C.RESULT_BUFFER_READ) {
if (buffer.isEndOfStream()) { if (buffer.isEndOfStream()) {
isEnded = true; isEnded = true;

View File

@ -24,9 +24,7 @@ import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.Arrays; import java.util.Arrays;
/** /** Fake {@link Timeline} which can be setup to return custom {@link TimelineWindowDefinition}s. */
* Fake {@link Timeline} which can be setup to return custom {@link TimelineWindowDefinition}s.
*/
public final class FakeTimeline extends Timeline { public final class FakeTimeline extends Timeline {
/** /**

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.testutil;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
@ -29,6 +30,8 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* A fake {@link TrackOutput}. * A fake {@link TrackOutput}.
@ -39,10 +42,10 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
private final ArrayList<Integer> sampleFlags; private final ArrayList<Integer> sampleFlags;
private final ArrayList<Integer> sampleStartOffsets; private final ArrayList<Integer> sampleStartOffsets;
private final ArrayList<Integer> sampleEndOffsets; private final ArrayList<Integer> sampleEndOffsets;
private final ArrayList<CryptoData> cryptoDatas; private final ArrayList<@NullableType CryptoData> cryptoDatas;
private byte[] sampleData; private byte[] sampleData;
public Format format; public @MonotonicNonNull Format format;
public FakeTrackOutput() { public FakeTrackOutput() {
sampleData = Util.EMPTY_BYTE_ARRAY; sampleData = Util.EMPTY_BYTE_ARRAY;
@ -64,6 +67,7 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
@Override @Override
public void format(Format format) { public void format(Format format) {
// TODO: Capture and assert all formats (interleaved with the samples).
this.format = format; this.format = format;
} }
@ -91,8 +95,12 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
} }
@Override @Override
public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset, public void sampleMetadata(
CryptoData cryptoData) { long timeUs,
@C.BufferFlags int flags,
int size,
int offset,
@Nullable CryptoData cryptoData) {
if (format == null) { if (format == null) {
throw new IllegalStateException("TrackOutput must receive format before sampleMetadata"); throw new IllegalStateException("TrackOutput must receive format before sampleMetadata");
} }
@ -110,7 +118,8 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
assertThat(sampleTimesUs).hasSize(count); assertThat(sampleTimesUs).hasSize(count);
} }
public void assertSample(int index, byte[] data, long timeUs, int flags, CryptoData cryptoData) { public void assertSample(
int index, byte[] data, long timeUs, int flags, @Nullable CryptoData cryptoData) {
byte[] actualData = getSampleData(index); byte[] actualData = getSampleData(index);
assertThat(actualData).isEqualTo(data); assertThat(actualData).isEqualTo(data);
assertThat(sampleTimesUs.get(index)).isEqualTo(timeUs); assertThat(sampleTimesUs.get(index)).isEqualTo(timeUs);
@ -131,6 +140,7 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
return sampleFlags.get(index); return sampleFlags.get(index);
} }
@Nullable
public CryptoData getSampleCryptoData(int index) { public CryptoData getSampleCryptoData(int index) {
return cryptoDatas.get(index); return cryptoDatas.get(index);
} }
@ -162,34 +172,36 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
@Override @Override
public void dump(Dumper dumper) { public void dump(Dumper dumper) {
dumper if (format != null) {
.startBlock("format") dumper
.add("bitrate", format.bitrate) .startBlock("format")
.add("id", format.id) .add("bitrate", format.bitrate)
.add("containerMimeType", format.containerMimeType) .add("id", format.id)
.add("sampleMimeType", format.sampleMimeType) .add("containerMimeType", format.containerMimeType)
.add("maxInputSize", format.maxInputSize) .add("sampleMimeType", format.sampleMimeType)
.add("width", format.width) .add("maxInputSize", format.maxInputSize)
.add("height", format.height) .add("width", format.width)
.add("frameRate", format.frameRate) .add("height", format.height)
.add("rotationDegrees", format.rotationDegrees) .add("frameRate", format.frameRate)
.add("pixelWidthHeightRatio", format.pixelWidthHeightRatio) .add("rotationDegrees", format.rotationDegrees)
.add("channelCount", format.channelCount) .add("pixelWidthHeightRatio", format.pixelWidthHeightRatio)
.add("sampleRate", format.sampleRate) .add("channelCount", format.channelCount)
.add("pcmEncoding", format.pcmEncoding) .add("sampleRate", format.sampleRate)
.add("encoderDelay", format.encoderDelay) .add("pcmEncoding", format.pcmEncoding)
.add("encoderPadding", format.encoderPadding) .add("encoderDelay", format.encoderDelay)
.add("subsampleOffsetUs", format.subsampleOffsetUs) .add("encoderPadding", format.encoderPadding)
.add("selectionFlags", format.selectionFlags) .add("subsampleOffsetUs", format.subsampleOffsetUs)
.add("language", format.language) .add("selectionFlags", format.selectionFlags)
.add("drmInitData", format.drmInitData != null ? format.drmInitData.hashCode() : "-") .add("language", format.language)
.add("metadata", format.metadata); .add("drmInitData", format.drmInitData != null ? format.drmInitData.hashCode() : "-")
.add("metadata", format.metadata);
dumper.startBlock("initializationData"); dumper.startBlock("initializationData");
for (int i = 0; i < format.initializationData.size(); i++) { for (int i = 0; i < format.initializationData.size(); i++) {
dumper.add("data", format.initializationData.get(i)); dumper.add("data", format.initializationData.get(i));
}
dumper.endBlock().endBlock();
} }
dumper.endBlock().endBlock();
dumper.add("total output bytes", sampleData.length); dumper.add("total output bytes", sampleData.length);
dumper.add("sample count", sampleTimesUs.size()); dumper.add("sample count", sampleTimesUs.size());

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.testutil;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
@ -109,6 +110,7 @@ public final class FakeTrackSelection implements TrackSelection {
} }
@Override @Override
@Nullable
public Object getSelectionData() { public Object getSelectionData() {
return null; return null;
} }

View File

@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import com.google.android.exoplayer2.RendererCapabilities.AdaptiveSupport;
import com.google.android.exoplayer2.RendererCapabilities.Capabilities;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
@ -23,6 +25,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.BandwidthMeter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** A fake {@link MappingTrackSelector} that returns {@link FakeTrackSelection}s. */ /** A fake {@link MappingTrackSelector} that returns {@link FakeTrackSelection}s. */
public class FakeTrackSelector extends DefaultTrackSelector { public class FakeTrackSelector extends DefaultTrackSelector {
@ -30,7 +33,7 @@ public class FakeTrackSelector extends DefaultTrackSelector {
private final FakeTrackSelectionFactory fakeTrackSelectionFactory; private final FakeTrackSelectionFactory fakeTrackSelectionFactory;
public FakeTrackSelector() { public FakeTrackSelector() {
this(false); this(/* mayReuseTrackSelection= */ false);
} }
/** /**
@ -48,13 +51,14 @@ public class FakeTrackSelector extends DefaultTrackSelector {
} }
@Override @Override
protected TrackSelection.Definition[] selectAllTracks( protected TrackSelection.@NullableType Definition[] selectAllTracks(
MappedTrackInfo mappedTrackInfo, MappedTrackInfo mappedTrackInfo,
int[][][] rendererFormatSupports, @Capabilities int[][][] rendererFormatSupports,
int[] rendererMixedMimeTypeAdaptationSupports, @AdaptiveSupport int[] rendererMixedMimeTypeAdaptationSupports,
Parameters params) { Parameters params) {
int rendererCount = mappedTrackInfo.getRendererCount(); int rendererCount = mappedTrackInfo.getRendererCount();
TrackSelection.Definition[] definitions = new TrackSelection.Definition[rendererCount]; TrackSelection.@NullableType Definition[] definitions =
new TrackSelection.Definition[rendererCount];
for (int i = 0; i < rendererCount; i++) { for (int i = 0; i < rendererCount; i++) {
TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(i); TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(i);
boolean hasTracks = trackGroupArray.length > 0; boolean hasTracks = trackGroupArray.length > 0;
@ -80,7 +84,7 @@ public class FakeTrackSelector extends DefaultTrackSelector {
@Override @Override
public TrackSelection[] createTrackSelections( public TrackSelection[] createTrackSelections(
TrackSelection.Definition[] definitions, BandwidthMeter bandwidthMeter) { @NullableType TrackSelection.Definition[] definitions, BandwidthMeter bandwidthMeter) {
TrackSelection[] selections = new TrackSelection[definitions.length]; TrackSelection[] selections = new TrackSelection[definitions.length];
for (int i = 0; i < definitions.length; i++) { for (int i = 0; i < definitions.length; i++) {
TrackSelection.Definition definition = definitions[i]; TrackSelection.Definition definition = definitions[i];

View File

@ -33,9 +33,7 @@ import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
/** /** A host activity for performing playback tests. */
* A host activity for performing playback tests.
*/
public final class HostActivity extends Activity implements SurfaceHolder.Callback { public final class HostActivity extends Activity implements SurfaceHolder.Callback {
/** /**

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.BasePlayer; import com.google.android.exoplayer2.BasePlayer;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
@ -250,7 +251,7 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
} }
@Override @Override
public void setPlaybackParameters(PlaybackParameters playbackParameters) { public void setPlaybackParameters(@Nullable PlaybackParameters playbackParameters) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -260,7 +261,7 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
} }
@Override @Override
public void setSeekParameters(SeekParameters seekParameters) { public void setSeekParameters(@Nullable SeekParameters seekParameters) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -18,13 +18,16 @@ package com.google.android.exoplayer2.testutil;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.offline.Download; import com.google.android.exoplayer2.offline.Download;
import com.google.android.exoplayer2.offline.Download.State; import com.google.android.exoplayer2.offline.Download.State;
import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.util.Util;
import java.util.HashMap; import java.util.HashMap;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** A {@link DownloadManager.Listener} for testing. */ /** A {@link DownloadManager.Listener} for testing. */
public final class TestDownloadManagerListener implements DownloadManager.Listener { public final class TestDownloadManagerListener implements DownloadManager.Listener {
@ -39,7 +42,7 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen
private final CountDownLatch initializedCondition; private final CountDownLatch initializedCondition;
private final int timeoutMs; private final int timeoutMs;
private CountDownLatch downloadFinishedCondition; private @MonotonicNonNull CountDownLatch downloadFinishedCondition;
@Download.FailureReason private int failureReason; @Download.FailureReason private int failureReason;
public TestDownloadManagerListener( public TestDownloadManagerListener(
@ -57,6 +60,7 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen
downloadManager.addListener(this); downloadManager.addListener(this);
} }
@Nullable
public Integer pollStateChange(String taskId, long timeoutMs) throws InterruptedException { public Integer pollStateChange(String taskId, long timeoutMs) throws InterruptedException {
return getStateQueue(taskId).poll(timeoutMs, TimeUnit.MILLISECONDS); return getStateQueue(taskId).poll(timeoutMs, TimeUnit.MILLISECONDS);
} }
@ -112,7 +116,7 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen
dummyMainThread.runOnMainThread( dummyMainThread.runOnMainThread(
() -> { () -> {
if (downloadManager.isIdle()) { if (downloadManager.isIdle()) {
downloadFinishedCondition.countDown(); Util.castNonNull(downloadFinishedCondition).countDown();
} }
}); });
assertThat(downloadFinishedCondition.await(timeoutMs, TimeUnit.MILLISECONDS)).isTrue(); assertThat(downloadFinishedCondition.await(timeoutMs, TimeUnit.MILLISECONDS)).isTrue();
@ -120,10 +124,12 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen
private ArrayBlockingQueue<Integer> getStateQueue(String taskId) { private ArrayBlockingQueue<Integer> getStateQueue(String taskId) {
synchronized (downloadStates) { synchronized (downloadStates) {
if (!downloadStates.containsKey(taskId)) { @Nullable ArrayBlockingQueue<Integer> stateQueue = downloadStates.get(taskId);
downloadStates.put(taskId, new ArrayBlockingQueue<>(10)); if (stateQueue == null) {
stateQueue = new ArrayBlockingQueue<>(10);
downloadStates.put(taskId, stateQueue);
} }
return downloadStates.get(taskId); return stateQueue;
} }
} }
@ -137,11 +143,11 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen
private void assertStateInternal(String taskId, int expectedState, int timeoutMs) { private void assertStateInternal(String taskId, int expectedState, int timeoutMs) {
while (true) { while (true) {
Integer state = null; @Nullable Integer state = null;
try { try {
state = pollStateChange(taskId, timeoutMs); state = pollStateChange(taskId, timeoutMs);
} catch (InterruptedException e) { } catch (InterruptedException e) {
fail(e.getMessage()); fail("Interrupted: " + e.getMessage());
} }
if (state != null) { if (state != null) {
if (expectedState == state) { if (expectedState == state) {

View File

@ -22,6 +22,8 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.Timeline.Window; import com.google.android.exoplayer2.Timeline.Window;
import com.google.android.exoplayer2.util.Assertions;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** Unit test for {@link Timeline}. */ /** Unit test for {@link Timeline}. */
public final class TimelineAsserts { public final class TimelineAsserts {
@ -48,7 +50,8 @@ public final class TimelineAsserts {
* @param expectedWindowTags A list of expected window tags. If a tag is unknown or not important * @param expectedWindowTags A list of expected window tags. If a tag is unknown or not important
* {@code null} can be passed to skip this window. * {@code null} can be passed to skip this window.
*/ */
public static void assertWindowTags(Timeline timeline, Object... expectedWindowTags) { public static void assertWindowTags(
Timeline timeline, @NullableType Object... expectedWindowTags) {
Window window = new Window(); Window window = new Window();
assertThat(timeline.getWindowCount()).isEqualTo(expectedWindowTags.length); assertThat(timeline.getWindowCount()).isEqualTo(expectedWindowTags.length);
for (int i = 0; i < timeline.getWindowCount(); i++) { for (int i = 0; i < timeline.getWindowCount(); i++) {
@ -140,8 +143,9 @@ public final class TimelineAsserts {
expectedWindowIndex++; expectedWindowIndex++;
} }
assertThat(period.windowIndex).isEqualTo(expectedWindowIndex); assertThat(period.windowIndex).isEqualTo(expectedWindowIndex);
assertThat(timeline.getIndexOfPeriod(period.uid)).isEqualTo(i); Object periodUid = Assertions.checkNotNull(period.uid);
assertThat(timeline.getUidOfPeriod(i)).isEqualTo(period.uid); assertThat(timeline.getIndexOfPeriod(periodUid)).isEqualTo(i);
assertThat(timeline.getUidOfPeriod(i)).isEqualTo(periodUid);
for (int repeatMode : REPEAT_MODES) { for (int repeatMode : REPEAT_MODES) {
if (i < accumulatedPeriodCounts[expectedWindowIndex + 1] - 1) { if (i < accumulatedPeriodCounts[expectedWindowIndex + 1] - 1) {
assertThat(timeline.getNextPeriodIndex(i, period, window, repeatMode, false)) assertThat(timeline.getNextPeriodIndex(i, period, window, repeatMode, false))