diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/IntArrayQueue.java b/libraries/common/src/main/java/androidx/media3/common/util/LongArrayQueue.java
similarity index 57%
rename from libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/IntArrayQueue.java
rename to libraries/common/src/main/java/androidx/media3/common/util/LongArrayQueue.java
index 5b70d116f7..5d2b47d45f 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/IntArrayQueue.java
+++ b/libraries/common/src/main/java/androidx/media3/common/util/LongArrayQueue.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,39 +13,57 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package androidx.media3.exoplayer.mediacodec;
+package androidx.media3.common.util;
-import androidx.media3.common.util.UnstableApi;
+import static androidx.media3.common.util.Assertions.checkArgument;
+
+import androidx.annotation.VisibleForTesting;
import java.util.NoSuchElementException;
/**
- * Array-based unbounded queue for int primitives with amortized O(1) add and remove.
+ * Array-based unbounded queue for long primitives with amortized O(1) add and remove.
*
- *
Use this class instead of a {@link java.util.Deque} to avoid boxing int primitives to {@link
- * Integer} instances.
+ *
Use this class instead of a {@link java.util.Deque} to avoid boxing long primitives to {@link
+ * Long} instances.
*/
@UnstableApi
-/* package */ final class IntArrayQueue {
+public final class LongArrayQueue {
- /** Default capacity needs to be a power of 2. */
- private static final int DEFAULT_INITIAL_CAPACITY = 16;
+ /** Default initial capacity. */
+ public static final int DEFAULT_INITIAL_CAPACITY = 16;
private int headIndex;
private int tailIndex;
private int size;
- private int[] data;
+ private long[] data;
private int wrapAroundMask;
- public IntArrayQueue() {
+ /** Creates a queue with an initial capacity of {@link #DEFAULT_INITIAL_CAPACITY}. */
+ public LongArrayQueue() {
+ this(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Creates a queue with capacity for at least {@code minCapacity}
+ *
+ * @param minCapacity minCapacity the minimum capacity, between 1 and 2^30 inclusive
+ */
+ public LongArrayQueue(int minCapacity) {
+ checkArgument(minCapacity >= 0 && minCapacity <= (1 << 30));
+ minCapacity = minCapacity == 0 ? 1 : minCapacity;
+ // If capacity isn't a power of 2, round up to the next highest power of 2.
+ if (Integer.bitCount(minCapacity) != 1) {
+ minCapacity = Integer.highestOneBit(minCapacity - 1) << 1;
+ }
headIndex = 0;
tailIndex = -1;
size = 0;
- data = new int[DEFAULT_INITIAL_CAPACITY];
+ data = new long[minCapacity];
wrapAroundMask = data.length - 1;
}
/** Add a new item to the queue. */
- public void add(int value) {
+ public void add(long value) {
if (size == data.length) {
doubleArraySize();
}
@@ -60,18 +78,31 @@ import java.util.NoSuchElementException;
*
* @throws NoSuchElementException if the queue is empty.
*/
- public int remove() {
+ public long remove() {
if (size == 0) {
throw new NoSuchElementException();
}
- int value = data[headIndex];
+ long value = data[headIndex];
headIndex = (headIndex + 1) & wrapAroundMask;
size--;
return value;
}
+ /**
+ * Retrieves, but does not remove, the head of the queue.
+ *
+ * @throws NoSuchElementException if the queue is empty.
+ */
+ public long element() {
+ if (size == 0) {
+ throw new NoSuchElementException();
+ }
+
+ return data[headIndex];
+ }
+
/** Returns the number of items in the queue. */
public int size() {
return size;
@@ -90,7 +121,8 @@ import java.util.NoSuchElementException;
}
/** Returns the length of the backing array. */
- public int capacity() {
+ @VisibleForTesting
+ /* package */ int capacity() {
return data.length;
}
@@ -100,7 +132,7 @@ import java.util.NoSuchElementException;
throw new IllegalStateException();
}
- int[] newData = new int[newCapacity];
+ long[] newData = new long[newCapacity];
int itemsToRight = data.length - headIndex;
int itemsToLeft = headIndex;
System.arraycopy(data, headIndex, newData, 0, itemsToRight);
diff --git a/libraries/common/src/test/java/androidx/media3/common/util/LongArrayQueueTest.java b/libraries/common/src/test/java/androidx/media3/common/util/LongArrayQueueTest.java
new file mode 100644
index 0000000000..5f5202651b
--- /dev/null
+++ b/libraries/common/src/test/java/androidx/media3/common/util/LongArrayQueueTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.media3.common.util;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import java.util.NoSuchElementException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link LongArrayQueue}. */
+@RunWith(AndroidJUnit4.class)
+public class LongArrayQueueTest {
+
+ @Test
+ public void capacity() {
+ LongArrayQueue queue = new LongArrayQueue(2);
+
+ assertThat(queue.capacity()).isEqualTo(2);
+ }
+
+ @Test
+ public void capacity_setTo0_increasedTo1() {
+ LongArrayQueue queue = new LongArrayQueue(0);
+
+ assertThat(queue.capacity()).isEqualTo(1);
+ }
+
+ @Test
+ public void capacity_setToNextPowerOf2() {
+ LongArrayQueue queue = new LongArrayQueue(6);
+
+ assertThat(queue.capacity()).isEqualTo(8);
+ }
+
+ @Test
+ public void capacity_invalidMinCapacity_throws() {
+ assertThrows(IllegalArgumentException.class, () -> new LongArrayQueue(-1));
+ }
+
+ @Test
+ public void add_beyondInitialCapacity_doublesCapacity() {
+ LongArrayQueue queue = new LongArrayQueue(2);
+
+ queue.add(0);
+ queue.add(1);
+ queue.add(2);
+
+ assertThat(queue.size()).isEqualTo(3);
+ assertThat(queue.capacity()).isEqualTo(4);
+ }
+
+ @Test
+ public void isEmpty_afterConstruction_returnsTrue() {
+ LongArrayQueue queue = new LongArrayQueue();
+
+ assertThat(queue.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void isEmpty_afterAddition_returnsFalse() {
+ LongArrayQueue queue = new LongArrayQueue();
+
+ queue.add(0);
+
+ assertThat(queue.isEmpty()).isFalse();
+ }
+
+ @Test
+ public void isEmpty_afterRemoval_returnsTrue() {
+ LongArrayQueue queue = new LongArrayQueue();
+
+ queue.add(0);
+ queue.remove();
+
+ assertThat(queue.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void remove_onEmptyQueue_throwsException() {
+ LongArrayQueue queue = new LongArrayQueue();
+
+ assertThrows(NoSuchElementException.class, queue::remove);
+ }
+
+ @Test
+ public void remove_returnsCorrectItem() {
+ LongArrayQueue queue = new LongArrayQueue();
+
+ queue.add(20);
+
+ assertThat(queue.remove()).isEqualTo(20);
+ }
+
+ @Test
+ public void element_withEmptyQueue_throws() {
+ LongArrayQueue queue = new LongArrayQueue();
+
+ assertThrows(NoSuchElementException.class, queue::element);
+ }
+
+ @Test
+ public void element_returnsQueueHead() {
+ LongArrayQueue queue = new LongArrayQueue();
+
+ queue.add(5);
+
+ assertThat(queue.element()).isEqualTo(5);
+ }
+
+ @Test
+ public void clear_resetsQueue() {
+ LongArrayQueue queue = new LongArrayQueue();
+
+ queue.add(123);
+ queue.clear();
+
+ assertThat(queue.size()).isEqualTo(0);
+ assertThat(queue.isEmpty()).isTrue();
+ }
+}
diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoCompositor.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoCompositor.java
index b041abf15a..88a2368931 100644
--- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoCompositor.java
+++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoCompositor.java
@@ -35,6 +35,7 @@ import androidx.media3.common.VideoFrameProcessingException;
import androidx.media3.common.util.GlProgram;
import androidx.media3.common.util.GlUtil;
import androidx.media3.common.util.Log;
+import androidx.media3.common.util.LongArrayQueue;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList;
@@ -81,8 +82,8 @@ public final class DefaultVideoCompositor implements VideoCompositor {
private boolean allInputsEnded; // Whether all inputSources have signaled end of input.
private final TexturePool outputTexturePool;
- private final Queue outputTextureTimestamps; // Synchronized with outputTexturePool.
- private final Queue syncObjects; // Synchronized with outputTexturePool.
+ private final LongArrayQueue outputTextureTimestamps; // Synchronized with outputTexturePool.
+ private final LongArrayQueue syncObjects; // Synchronized with outputTexturePool.
// Only used on the GL Thread.
private @MonotonicNonNull EGLContext eglContext;
@@ -111,8 +112,8 @@ public final class DefaultVideoCompositor implements VideoCompositor {
inputSources = new ArrayList<>();
outputTexturePool =
new TexturePool(/* useHighPrecisionColorComponents= */ false, textureOutputCapacity);
- outputTextureTimestamps = new ArrayDeque<>(textureOutputCapacity);
- syncObjects = new ArrayDeque<>(textureOutputCapacity);
+ outputTextureTimestamps = new LongArrayQueue(textureOutputCapacity);
+ syncObjects = new LongArrayQueue(textureOutputCapacity);
boolean ownsExecutor = executorService == null;
ExecutorService instanceExecutorService =
@@ -374,7 +375,7 @@ public final class DefaultVideoCompositor implements VideoCompositor {
private synchronized void releaseOutputFrameInternal(long presentationTimeUs)
throws VideoFrameProcessingException, GlUtil.GlException {
while (outputTexturePool.freeTextureCount() < outputTexturePool.capacity()
- && checkNotNull(outputTextureTimestamps.peek()) <= presentationTimeUs) {
+ && outputTextureTimestamps.element() <= presentationTimeUs) {
outputTexturePool.freeTexture();
outputTextureTimestamps.remove();
GlUtil.deleteSyncObject(syncObjects.remove());
diff --git a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java
index 784077e899..919f358c01 100644
--- a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java
+++ b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java
@@ -41,10 +41,10 @@ import androidx.media3.common.VideoFrameProcessingException;
import androidx.media3.common.VideoFrameProcessor;
import androidx.media3.common.util.GlUtil;
import androidx.media3.common.util.Log;
+import androidx.media3.common.util.LongArrayQueue;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
@@ -88,8 +88,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final VideoFrameProcessor.Listener videoFrameProcessorListener;
private final Queue> availableFrames;
private final TexturePool outputTexturePool;
- private final Queue outputTextureTimestamps; // Synchronized with outputTexturePool.
- private final Queue syncObjects;
+ private final LongArrayQueue outputTextureTimestamps; // Synchronized with outputTexturePool.
+ private final LongArrayQueue syncObjects;
@Nullable private final DefaultVideoFrameProcessor.TextureOutputListener textureOutputListener;
private int inputWidth;
@@ -148,8 +148,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
boolean useHighPrecisionColorComponents = ColorInfo.isTransferHdr(outputColorInfo);
outputTexturePool = new TexturePool(useHighPrecisionColorComponents, textureOutputCapacity);
- outputTextureTimestamps = new ArrayDeque<>(textureOutputCapacity);
- syncObjects = new ArrayDeque<>(textureOutputCapacity);
+ outputTextureTimestamps = new LongArrayQueue(textureOutputCapacity);
+ syncObjects = new LongArrayQueue(textureOutputCapacity);
}
@Override
@@ -227,7 +227,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private void releaseOutputFrameInternal(long presentationTimeUs) throws GlUtil.GlException {
checkState(textureOutputListener != null);
while (outputTexturePool.freeTextureCount() < outputTexturePool.capacity()
- && checkNotNull(outputTextureTimestamps.peek()) <= presentationTimeUs) {
+ && outputTextureTimestamps.element() <= presentationTimeUs) {
outputTexturePool.freeTexture();
outputTextureTimestamps.remove();
GlUtil.deleteSyncObject(syncObjects.remove());
diff --git a/libraries/exoplayer/build.gradle b/libraries/exoplayer/build.gradle
index 730cd5bf8b..fecd9bcbf9 100644
--- a/libraries/exoplayer/build.gradle
+++ b/libraries/exoplayer/build.gradle
@@ -50,6 +50,7 @@ dependencies {
api project(modulePrefix + 'lib-extractor')
api project(modulePrefix + 'lib-database')
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
+ implementation 'androidx.collection:collection:' + androidxCollectionVersion
implementation 'androidx.core:core:' + androidxCoreVersion
implementation 'androidx.exifinterface:exifinterface:1.3.6'
compileOnly 'com.google.code.findbugs:jsr305:' + jsr305Version
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java
index 922771b1d6..5b595ffa89 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java
@@ -26,6 +26,7 @@ import android.os.HandlerThread;
import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import androidx.collection.CircularIntArray;
import androidx.media3.common.util.Util;
import java.util.ArrayDeque;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@@ -39,10 +40,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private @MonotonicNonNull Handler handler;
@GuardedBy("lock")
- private final IntArrayQueue availableInputBuffers;
+ private final CircularIntArray availableInputBuffers;
@GuardedBy("lock")
- private final IntArrayQueue availableOutputBuffers;
+ private final CircularIntArray availableOutputBuffers;
@GuardedBy("lock")
private final ArrayDeque bufferInfos;
@@ -81,8 +82,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/* package */ AsynchronousMediaCodecCallback(HandlerThread callbackThread) {
this.lock = new Object();
this.callbackThread = callbackThread;
- this.availableInputBuffers = new IntArrayQueue();
- this.availableOutputBuffers = new IntArrayQueue();
+ this.availableInputBuffers = new CircularIntArray();
+ this.availableOutputBuffers = new CircularIntArray();
this.bufferInfos = new ArrayDeque<>();
this.formats = new ArrayDeque<>();
}
@@ -132,7 +133,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
} else {
return availableInputBuffers.isEmpty()
? MediaCodec.INFO_TRY_AGAIN_LATER
- : availableInputBuffers.remove();
+ : availableInputBuffers.popFirst();
}
}
}
@@ -152,7 +153,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
if (availableOutputBuffers.isEmpty()) {
return MediaCodec.INFO_TRY_AGAIN_LATER;
} else {
- int bufferIndex = availableOutputBuffers.remove();
+ int bufferIndex = availableOutputBuffers.popFirst();
if (bufferIndex >= 0) {
checkStateNotNull(currentFormat);
MediaCodec.BufferInfo nextBufferInfo = bufferInfos.remove();
@@ -204,7 +205,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
public void onInputBufferAvailable(MediaCodec codec, int index) {
synchronized (lock) {
- availableInputBuffers.add(index);
+ availableInputBuffers.addLast(index);
}
}
@@ -215,7 +216,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
addOutputFormat(pendingOutputFormat);
pendingOutputFormat = null;
}
- availableOutputBuffers.add(index);
+ availableOutputBuffers.addLast(index);
bufferInfos.add(info);
}
}
@@ -278,7 +279,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@GuardedBy("lock")
private void addOutputFormat(MediaFormat mediaFormat) {
- availableOutputBuffers.add(MediaCodec.INFO_OUTPUT_FORMAT_CHANGED);
+ availableOutputBuffers.addLast(MediaCodec.INFO_OUTPUT_FORMAT_CHANGED);
formats.add(mediaFormat);
}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java
index cdfc279988..b5de31258c 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java
@@ -37,13 +37,13 @@ import androidx.media3.common.SurfaceInfo;
import androidx.media3.common.VideoFrameProcessingException;
import androidx.media3.common.VideoFrameProcessor;
import androidx.media3.common.VideoSize;
+import androidx.media3.common.util.LongArrayQueue;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.TimedValueQueue;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@@ -168,8 +168,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final Context context;
private final RenderControl renderControl;
private final VideoFrameProcessor videoFrameProcessor;
- // TODO b/293447478 - Use a queue for primitive longs to avoid the cost of boxing to Long.
- private final ArrayDeque processedFramesBufferTimestampsUs;
+ private final LongArrayQueue processedFramesBufferTimestampsUs;
private final TimedValueQueue streamOffsets;
private final TimedValueQueue videoSizeChanges;
private final Handler handler;
@@ -219,7 +218,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
throws VideoFrameProcessingException {
this.context = context;
this.renderControl = renderControl;
- processedFramesBufferTimestampsUs = new ArrayDeque<>();
+ processedFramesBufferTimestampsUs = new LongArrayQueue();
streamOffsets = new TimedValueQueue<>();
videoSizeChanges = new TimedValueQueue<>();
// TODO b/226330223 - Investigate increasing frame count when frame dropping is
@@ -362,7 +361,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
public void render(long positionUs, long elapsedRealtimeUs) {
while (!processedFramesBufferTimestampsUs.isEmpty()) {
- long bufferPresentationTimeUs = checkNotNull(processedFramesBufferTimestampsUs.peek());
+ long bufferPresentationTimeUs = processedFramesBufferTimestampsUs.element();
// check whether this buffer comes with a new stream offset.
if (maybeUpdateOutputStreamOffset(bufferPresentationTimeUs)) {
renderedFirstFrame = false;
diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/IntArrayQueueTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/IntArrayQueueTest.java
deleted file mode 100644
index c197230b35..0000000000
--- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/IntArrayQueueTest.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package androidx.media3.exoplayer.mediacodec;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import java.util.NoSuchElementException;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/** Tests for {@link IntArrayQueue}. */
-@RunWith(AndroidJUnit4.class)
-public class IntArrayQueueTest {
-
- @Test
- public void add_willDoubleCapacity() {
- IntArrayQueue queue = new IntArrayQueue();
- int capacity = queue.capacity();
-
- for (int i = 0; i <= capacity; i++) {
- queue.add(i);
- }
-
- assertThat(queue.capacity()).isEqualTo(2 * capacity);
- assertThat(queue.size()).isEqualTo(capacity + 1);
- }
-
- @Test
- public void isEmpty_returnsTrueAfterConstruction() {
- IntArrayQueue queue = new IntArrayQueue();
-
- assertThat(queue.isEmpty()).isTrue();
- }
-
- @Test
- public void isEmpty_returnsFalseAfterAddition() {
- IntArrayQueue queue = new IntArrayQueue();
- queue.add(0);
-
- assertThat(queue.isEmpty()).isFalse();
- }
-
- @Test
- public void isEmpty_returnsFalseAfterRemoval() {
- IntArrayQueue queue = new IntArrayQueue();
- queue.add(0);
- queue.remove();
-
- assertThat(queue.isEmpty()).isTrue();
- }
-
- @Test
- public void remove_onEmptyQueue_throwsException() {
- IntArrayQueue queue = new IntArrayQueue();
-
- try {
- queue.remove();
- fail();
- } catch (NoSuchElementException expected) {
- // expected
- }
- }
-
- @Test
- public void remove_returnsCorrectItem() {
- IntArrayQueue queue = new IntArrayQueue();
- int value = 20;
- queue.add(value);
-
- assertThat(queue.remove()).isEqualTo(value);
- }
-
- @Test
- public void remove_untilIsEmpty() {
- IntArrayQueue queue = new IntArrayQueue();
- for (int i = 0; i < 1024; i++) {
- queue.add(i);
- }
-
- int expectedRemoved = 0;
- while (!queue.isEmpty()) {
- if (expectedRemoved == 15) {
- System.out.println("foo");
- }
- int removed = queue.remove();
- assertThat(removed).isEqualTo(expectedRemoved++);
- }
- }
-
- @Test
- public void remove_withResize_returnsCorrectItem() {
- IntArrayQueue queue = new IntArrayQueue();
- int nextToAdd = 0;
-
- while (queue.size() < queue.capacity()) {
- queue.add(nextToAdd++);
- }
-
- queue.remove();
- queue.remove();
-
- // This will force the queue to wrap-around and then resize
- int howManyToResize = queue.capacity() - queue.size() + 1;
- for (int i = 0; i < howManyToResize; i++) {
- queue.add(nextToAdd++);
- }
-
- assertThat(queue.remove()).isEqualTo(2);
- }
-
- @Test
- public void clear_resetsQueue() {
- IntArrayQueue queue = new IntArrayQueue();
-
- // Add items until array re-sizes twice (capacity grows by 4)
- for (int i = 0; i < 1024; i++) {
- queue.add(i);
- }
-
- queue.clear();
-
- assertThat(queue.size()).isEqualTo(0);
- assertThat(queue.isEmpty()).isTrue();
- }
-}