diff --git a/javalib/src/main/scala/java/util/ArrayDeque.scala b/javalib/src/main/scala/java/util/ArrayDeque.scala new file mode 100644 index 0000000000..54567104ce --- /dev/null +++ b/javalib/src/main/scala/java/util/ArrayDeque.scala @@ -0,0 +1,232 @@ +// Ported from Scala.js. +// Also contains original work for Scala Native. + +package java.util + +/// ScalaNative Porting Note: +/// +/// * Ported, with thanks & gratitude, from Scala.js ArrayDeque.scala +/// commit 9DC4D5b, dated 2018-10-12. +/// Also contains original work for Scala Native. +/// +/// * Changes in Scala.js original commit E07F99D, dated 2019-07-30 +/// were considered on 2020-05-19. The Scala.js changes to +/// ArrayDeque.scala were to use Objects.equals() in 3 places: +/// contains(), removeFirstOccurrence(), & removeLastOccurrence(). +/// No corresponding change is needed here because the above +/// methods of this class are defined in terms of +/// inner.{contains,indexOf,lastIndexOf}. inner is a +/// java.util.ArrayList, whose methods already use the semantics of +/// Object.equals(). +/// +/// * ArrayList is the inner type, rather than js.Array. +/// +/// * The order of method declarations is not alphabetical to reduce +/// churn versus Scala.js original. + +class ArrayDeque[E] private (private val inner: ArrayList[E]) + extends AbstractCollection[E] + with Deque[E] + with Cloneable + with Serializable { + self => + + private var status = 0 + + def this() = + this(new ArrayList[E](16)) + + def this(initialCapacity: Int) = { + // This is the JVM behavior for negative initialCapacity. + this(new ArrayList[E](Math.max(0, initialCapacity))) + } + + def this(c: Collection[_ <: E]) = { + this(c.size) + addAll(c) + } + + override def add(e: E): Boolean = { + offerLast(e) + true + } + + def addFirst(e: E): Unit = + offerFirst(e) + + def addLast(e: E): Unit = + offerLast(e) + + // shallow-copy + override def clone(): ArrayDeque[E] = + new ArrayDeque[E](inner.clone.asInstanceOf[ArrayList[E]]) + + def offerFirst(e: E): Boolean = { + if (e == null) { + throw new NullPointerException() + } else { + inner.add(0, e) + status += 1 + true + } + } + + def offerLast(e: E): Boolean = { + if (e == null) { + throw new NullPointerException() + } else { + inner.add(e) + status += 1 + true + } + } + + def removeFirst(): E = { + if (inner.isEmpty) + throw new NoSuchElementException() + else + pollFirst() + } + + def removeLast(): E = { + if (inner.isEmpty) + throw new NoSuchElementException() + else + pollLast() + } + + def pollFirst(): E = { + if (inner.isEmpty) null.asInstanceOf[E] + else { + val res = inner.remove(0) + status += 1 + res + } + } + + def pollLast(): E = { + if (inner.isEmpty) null.asInstanceOf[E] + else { + val res = inner.remove(inner.size - 1) + status += 1 + res + } + } + + def getFirst(): E = { + if (inner.isEmpty) + throw new NoSuchElementException() + else + peekFirst() + } + + def getLast(): E = { + if (inner.isEmpty) + throw new NoSuchElementException() + else + peekLast() + } + + def peekFirst(): E = { + if (inner.isEmpty) null.asInstanceOf[E] + else inner.get(0) + } + + def peekLast(): E = { + if (inner.isEmpty) null.asInstanceOf[E] + else inner.get(inner.size - 1) + } + + def removeFirstOccurrence(o: Any): Boolean = { + val index = inner.indexOf(o) + if (index >= 0) { + inner.remove(index) + status += 1 + true + } else + false + } + + def removeLastOccurrence(o: Any): Boolean = { + val index = inner.lastIndexOf(o) + if (index >= 0) { + inner.remove(index) + status += 1 + true + } else + false + } + + def offer(e: E): Boolean = offerLast(e) + + def remove(): E = removeFirst() + + def poll(): E = pollFirst() + + def element(): E = getFirst() + + def peek(): E = peekFirst() + + def push(e: E): Unit = addFirst(e) + + def pop(): E = removeFirst() + + def size(): Int = inner.size + + private def failFastIterator(startIndex: Int, nex: (Int) => Int) = { + new Iterator[E] { + private def checkStatus() = { + if (self.status != actualStatus) + throw new ConcurrentModificationException() + } + + private val actualStatus = self.status + + private var index: Int = startIndex + + def hasNext(): Boolean = { + checkStatus() + val n = nex(index) + (n >= 0) && (n < inner.size) + } + + def next(): E = { + checkStatus() + index = nex(index) + inner.get(index) + } + + def remove(): Unit = { + checkStatus() + if (index < 0 || index >= inner.size) { + throw new IllegalStateException() + } else { + inner.remove(index) + } + } + } + } + + def iterator(): Iterator[E] = + failFastIterator(-1, x => (x + 1)) + + def descendingIterator(): Iterator[E] = + failFastIterator(inner.size, x => (x - 1)) + + override def contains(o: Any): Boolean = inner.contains(o) + + override def remove(o: Any): Boolean = removeFirstOccurrence(o) + + override def clear(): Unit = { + if (!inner.isEmpty) status += 1 + inner.clear() + } + + override def toArray(): Array[AnyRef] = { + inner.toArray() + } + + override def toArray[T](a: Array[T]): Array[T] = { + inner.toArray(a) + } +} diff --git a/unit-tests/src/test/scala/java/util/ArrayDequeSuite.scala b/unit-tests/src/test/scala/java/util/ArrayDequeSuite.scala new file mode 100644 index 0000000000..8c10512425 --- /dev/null +++ b/unit-tests/src/test/scala/java/util/ArrayDequeSuite.scala @@ -0,0 +1,884 @@ +package java.util + +import scala.collection.JavaConverters._ + +object ArrayDequeSuite extends tests.Suite { + + test("ArrayDeque()") { + val ad = new ArrayDeque() + + assert(ad != null, s"Constructor returned null") + + // There is no good way to check underlying capacity, which should + // be 16. + + assert(ad.isEmpty(), s"constructed ArrayDeque() is not empty") + + val resultSize = ad.size + val expectedSize = 0 + assert(resultSize == expectedSize, + s"size: ${resultSize} != expected: ${expectedSize}") + } + + test("ArrayDeque(initialCapacity) - capacity >= 0") { + val ad = new ArrayDeque(20) + + assert(ad != null, s"Constructor returned null") + + // There is no good way to check underlying capacity, which should + // be 20. + + assert(ad.isEmpty(), s"constructed ArrayDeque() is not empty") + + val resultSize = ad.size + val expectedSize = 0 + assert(resultSize == expectedSize, + s"size: ${resultSize} != expected: ${expectedSize}") + } + + test("ArrayDeque(initialCapacity) - capacity < 0") { + // This test basically tests that no exception is thrown + // when the initialCapacity is negative, implementing JVM behavior. + + val ad = new ArrayDeque(-1) + + assert(ad != null, s"Constructor returned null") + + // There is no good way to check underlying capacity, which should + // be 20. + + assert(ad.isEmpty(), s"constructed ArrayDeque() is not empty") + + val resultSize = ad.size + val expectedSize = 0 + assert(resultSize == expectedSize, + s"size: ${resultSize} != expected: ${expectedSize}") + } + + test("ArrayDeque(null)") { + assertThrows[NullPointerException] { + new ArrayDeque(null) + } + } + + test("ArrayDeque(Collection[java.lang.Integer])") { // for AnyVal + val is = Seq(1, 2, 3) + val ad = new ArrayDeque(is.asJava) + assert(ad.size() == 3, "a1") + assert(!ad.isEmpty(), "a2") + + val result = ad.toArray + val expected = is.toArray + assert(result.sameElements(expected), + s"element: ${result} != expected: ${expected})") + } + + test("ArrayDeque(Collection[String])") { // for AnyRef + val is = Seq(1, 2, 3).map(_.toString) + import scala.collection.JavaConverters._ + val ad = new ArrayDeque(is.asJava) + assert(ad.size() == 3, "a1") + assert(!ad.isEmpty(), "a2") + + val result = ad.toArray + val expected = is.toArray + + assert(result.sameElements(expected), + s"element: ${result} != expected: ${expected})") + } + + test(s"add(e) - trigger capacity change") { + // Simple add()s are triggered by the addAll() in the previous + // ArrayDesueue(constructor) test. Exercise a more complex code path. + // Code should not fail when it resizes when adding the 17th element. + + val max = 20 // Must be > 16 + val is = 1 to 20 + val ad = new ArrayDeque[Int]() + + for (e <- is) { + ad.add(e) + } + + for (e <- is) { + val result = ad.removeFirst() + val expected = e + assert(result == expected, s"element: ${result} != expected: ${expected}") + } + } + + test(s"addFirst(e)") { + locally { + type E = Float + val ad = new ArrayDeque[E]() + + assertThrows[NullPointerException] { + ad.addFirst(null.asInstanceOf[E]) + } + } + + locally { + val is = Seq(-1, -2) + val ad = new ArrayDeque[Int]() + + ad.add(is(0)) + ad.addFirst(is(1)) + + val result = ad.toArray + val expected = is.reverse.toArray + + assert(result.sameElements(expected), + s"result: ${ad.toString} != " + + s"expected: ${expected.mkString("[", ", ", "]")}") + } + } + + test(s"addLast(e)") { + locally { + type E = Long + val ad = new ArrayDeque[E]() + + assertThrows[NullPointerException] { + ad.addLast(null.asInstanceOf[E]) + } + } + + locally { + val expected = Array(-1, -2) + val ad = new ArrayDeque[Int]() + + ad.add(expected(0)) + ad.addLast(expected(1)) + + val result = ad.toArray + + assert(result.sameElements(expected), + s"result: ${ad.toString} != " + + s"expected: ${expected.mkString("[", ", ", "]")}") + } + } + + test(s"clear()") { + val ad1 = new ArrayDeque(Seq(1, 2, 3, 2).asJava) + ad1.clear() + assert(ad1.isEmpty()) + // makes sure that clear()ing an already empty list is safe. + ad1.clear() + } + + test(s"clone()") { + val ad1 = new ArrayDeque(Seq(1, 2, 3, 2).asJava) + val ad2 = ad1.clone() + + val element = 1 + + assert(!ad1.eq(ad2), "must be different objects") + assert(ad1.toString == ad2.toString, "must have same contents") + + ad1.add(element) + assert(ad1.toString != ad2.toString, "must have different contents") + ad2.add(element) + assert(ad1.toString == ad2.toString, "must have same contents") + } + + test(s"contains(o: Any)") { + val needle = Math.PI + val is = Seq(1.1, 2.2, 3.3, needle, 4.0) + val ad = new ArrayDeque(is.asJava) + + val result = ad.contains(needle) + assert(result, s"'${ad.toString}' does not contain '${needle}'") + } + + test(s"descendingIterator()") { + // No good way on single threaded ScalaNative to test for + // ConcurrentModificationException + + val is = Seq(1, 2, 3) + val ad = new ArrayDeque(is.asJava) + + val result = ad.descendingIterator.asScala.toArray + val expected = is.reverse.toArray + + assert(result.sameElements(expected), + s"element: result} != expected: ${expected})") + } + + test(s"element()") { + locally { + val ad = new ArrayDeque() + + assertThrows[NoSuchElementException] { + ad.getFirst + } + } + + locally { + val is = Seq(33, 22, 11) + val ad = new ArrayDeque(is.asJava) + + val result = ad.element + + val expected = is.head + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + test(s"getFirst()") { + locally { + val ad = new ArrayDeque() + + assertThrows[NoSuchElementException] { + ad.getFirst + } + } + + locally { + val is = Seq("33", "22", "11") + val ad = new ArrayDeque(is.asJava) + + val result = ad.getFirst + + val expected = is.head + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + test(s"getLast()") { + locally { + val ad = new ArrayDeque() + + assertThrows[NoSuchElementException] { + ad.getFirst + } + } + + locally { + val is = Seq(-33, -22, -11) + val ad = new ArrayDeque(is.asJava) + + val result = ad.getLast + + val expected = is.last + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + // test(s"isEmpty()") exercised in ArrayDeque constructors + + test(s"iterator()") { + // No good way on single threaded ScalaNative to test for + // ConcurrentModificationException + + val is = Seq(-11, 0, 1) + val ad = new ArrayDeque(is.asJava) + + val result = ad.iterator.asScala.toArray + val expected = is.toArray + + assert(result.sameElements(expected), + s"element: ${result} != expected: ${expected})") + } + + test(s"offer(e: E)") { + locally { + type E = Long + val ad = new ArrayDeque[E]() + + assertThrows[NullPointerException] { + ad.offer(null.asInstanceOf[E]) + } + } + + locally { + val expected = Array(-1, -2) + val ad = new ArrayDeque[Int]() + + ad.offer(expected(0)) + ad.offer(expected(1)) + + val result = ad.toArray + + assert(result.sameElements(expected), + s"result: ${ad.toString} != " + + s"expected: ${expected.mkString("[", ", ", "]")}") + } + } + + test(s"offerFirst(e: E)") { + locally { + type E = Float + val ad = new ArrayDeque[E]() + + assertThrows[NullPointerException] { + ad.offerFirst(null.asInstanceOf[E]) + } + } + + locally { + val is = Seq(-1, -2) + val ad = new ArrayDeque[Int]() + + ad.offer(is(0)) + ad.offerFirst(is(1)) + + val result = ad.toArray + val expected = is.reverse.toArray + + assert(result.sameElements(expected), + s"result: ${ad.toString} != " + + s"expected: ${expected.mkString("[", ", ", "]")}") + } + } + + test(s"offerLast(e: E)") { + locally { + type E = Long + val ad = new ArrayDeque[E]() + + assertThrows[NullPointerException] { + ad.offerLast(null.asInstanceOf[E]) + } + } + + locally { + val expected = Array(-1, -2) + val ad = new ArrayDeque[Int]() + + ad.offerLast(expected(0)) + ad.offerLast(expected(1)) + + val result = ad.toArray + + assert(result.sameElements(expected), + s"result: ${ad.toString} != " + + s"expected: ${expected.mkString("[", ", ", "]")}") + } + } + + test(s"peek()") { + locally { + val ad = new ArrayDeque() + + assert(ad.peek == null, + s"expected null from peek() with empty ArrayDeque") + } + + locally { + val is = Seq("33", "22", "11") + val ad = new ArrayDeque(is.asJava) + + val result = ad.peek + + val expected = is.head + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + test(s"peekFirst()") { + locally { + val ad = new ArrayDeque() + + assert(ad.peekFirst == null, + s"expected null from peekFirst() with empty ArrayDeque") + } + + locally { + val is = Seq("33", "22", "11") + val ad = new ArrayDeque(is.asJava) + + val result = ad.peekFirst + + val expected = is.head + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + test(s"peekLast()") { + locally { + val ad = new ArrayDeque() + + assert(ad.peekLast == null, + s"expected null from peekFirst() with empty ArrayDeque") + } + + locally { + val is = Seq(-33, -22, -11) + val ad = new ArrayDeque(is.asJava) + + val result = ad.peekLast + + val expected = is.last + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + test(s"poll()") { + locally { + val ad = new ArrayDeque() + + assert(ad.poll == null, + s"expected null from poll() with empty ArrayDeque") + } + + locally { + val is = Seq(33, 22, 11) + val ad = new ArrayDeque(is.asJava) + + val result = ad.poll + + val expected = is.head + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size - 1 + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + test(s"pollFirst()") { + locally { + val ad = new ArrayDeque() + + assert(ad.pollFirst == null, + s"expected null from pollFirst() with empty ArrayDeque") + } + + locally { + val is = Seq(33, 22, 11) + val ad = new ArrayDeque(is.asJava) + + val result = ad.pollFirst + + val expected = is.head + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size - 1 + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + test(s"pollLast()") { + locally { + val ad = new ArrayDeque() + assert(ad.pollLast == null, + s"expected null from pollLast() with empty ArrayDeque") + } + + locally { + val is = Seq(-33, -22, -11) + val ad = new ArrayDeque(is.asJava) + + val result = ad.pollLast + + val expected = is.last + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size - 1 + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + test(s"pop()") { + locally { + val ad = new ArrayDeque() + + assertThrows[NoSuchElementException] { + ad.pop + } + } + + locally { + val is = Seq(33, 22, 11) + val ad = new ArrayDeque(is.asJava) + + val result = ad.pop + + val expected = is.head + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size - 1 + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + test(s"push(e: E)") { + locally { + type E = Double + val ad = new ArrayDeque[E]() + + assertThrows[NullPointerException] { + ad.push(null.asInstanceOf[E]) + } + } + + locally { + val is = Seq(-1, -2) + val ad = new ArrayDeque[Int]() + + ad.add(is(0)) + ad.push(is(1)) + + val result = ad.toArray + val expected = is.reverse.toArray + + assert(result.sameElements(expected), + s"result: ${ad.toString} != " + + s"expected: ${expected.mkString("[", ", ", "]")}") + } + } + + test(s"remove()") { + locally { + val ad = new ArrayDeque() + + assertThrows[NoSuchElementException] { + ad.remove + } + } + + locally { + val is = Seq(33, 22, 11) + val ad = new ArrayDeque(is.asJava) + + val result = ad.remove + + val expected = is.head + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size - 1 + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + test(s"remove(o: Any)") { + val haystack = "Looking for a needle in a haystack" + val words = haystack.split(" ").toSeq + val ad = new ArrayDeque(words.asJava) + + locally { + val adClone = ad.clone() + val adCloneStr = adClone.toString + + assert(ad.toString == adClone.toString, + "deque and its clone must have same contents") + + val beforeSize = ad.size + val needle = "sharp" + + val result = ad.remove(needle) + + assert(!result, s"word '${needle}' found in string '${haystack}'") + + // Show deque has not changed + + val afterSize = ad.size + val expectedSize = beforeSize + + assert(afterSize == expectedSize, + s"size: ${afterSize} != expected: ${beforeSize}") + + val adStr = ad.toString + assert(ad.toString == adCloneStr, + "deque: ${adStr} != expected: '${adCloneStr}'") + } + + locally { + val needle = "needle" + val beforeSize = ad.size + + val result = ad.remove(needle) + + assert(result, s"word '${needle}' not found in string '${haystack}'") + + // Show deque changed as expected. + + val afterSize = ad.size + val expectedSize = beforeSize - 1 + + assert(afterSize == expectedSize, + s"size: ${afterSize} != expected: ${beforeSize}") + + val adStr = ad.toString + + assert(!ad.toString.contains(needle), + "deque: ${adStr} must not contain '${needle}'") + } + } + + test(s"removeFirst()") { + locally { + val ad = new ArrayDeque() + + assertThrows[NoSuchElementException] { + ad.removeFirst + } + } + + locally { + val is = Seq(33, 22, 11) + val ad = new ArrayDeque(is.asJava) + + val result = ad.removeFirst + + val expected = is.head + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size - 1 + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + test(s"removeFirstOccurrence(o: Any)") { + val haystack = "Square needle || round needle || shiny needle" + val words = haystack.split(" ").toSeq + val ad = new ArrayDeque(words.asJava) + + locally { + val adClone = ad.clone() + val adCloneStr = adClone.toString + + assert(ad.toString == adClone.toString, + "deque and its clone must have same contents") + + val beforeSize = ad.size + val needle = "sharp" + + val result = ad.removeFirstOccurrence(needle) + + assert(!result, s"word '${needle}' found in string '${haystack}'") + + // Show deque has not changed + + val afterSize = ad.size + val expectedSize = beforeSize + + assert(afterSize == expectedSize, + s"size: ${afterSize} != expected: ${beforeSize}") + + val adStr = ad.toString + assert(ad.toString == adCloneStr, + "deque: ${adStr} != expected: '${adCloneStr}'") + } + + locally { + val needle = "needle" + val beforeSize = ad.size + + val result = ad.removeFirstOccurrence(needle) + + assert(result, s"word '${needle}' not found in string '${haystack}'") + + // Show deque changed as expected. + + val afterSize = ad.size + val expectedSize = beforeSize - 1 + + assert(afterSize == expectedSize, + s"size: ${afterSize} != expected: ${beforeSize}") + + for (i <- 0 until words.length if i != 1) { + val result = ad.removeFirst + val expected = words(i) + assert(result == expected, + "deque(${i}): ${result} != expected: '${expected}'") + } + } + } + + test(s"removeLast()") { + locally { + val ad = new ArrayDeque() + + assertThrows[NoSuchElementException] { + ad.removeLast + } + } + + locally { + val is = Seq(-33, -22, -11) + val ad = new ArrayDeque(is.asJava) + + val result = ad.removeLast + + val expected = is.last + + assert(result == expected, s"result: ${result} != expected: ${expected}") + + val afterSize = ad.size + val expectedSize = is.size - 1 + assert(afterSize == expectedSize, + s"after size: ${afterSize} != expected: ${expectedSize}") + } + } + + test(s"removeLastOccurrence(o: Any)") { + val haystack = "Square needle || round needle || shiny needle" + val words = haystack.split(" ").toSeq + val ad = new ArrayDeque(words.asJava) + + locally { + val adClone = ad.clone() + val adCloneStr = adClone.toString + + assert(ad.toString == adClone.toString, + "deque and its clone must have same contents") + + val beforeSize = ad.size + val needle = "sharp" + + val result = ad.removeLastOccurrence(needle) + + assert(!result, s"word '${needle}' found in string '${haystack}'") + + // Show deque has not changed + + val afterSize = ad.size + val expectedSize = beforeSize + + assert(afterSize == expectedSize, + s"size: ${afterSize} != expected: ${beforeSize}") + + val adStr = ad.toString + assert(ad.toString == adCloneStr, + "deque: ${adStr} != expected: '${adCloneStr}'") + } + + locally { + val needle = "needle" + val beforeSize = ad.size + + val result = ad.removeLastOccurrence(needle) + + assert(result, s"word '${needle}' not found in string '${haystack}'") + + // Show deque changed as expected. + + val afterSize = ad.size + val expectedSize = beforeSize - 1 + + assert(afterSize == expectedSize, + s"size: ${afterSize} != expected: ${beforeSize}") + + for (i <- 0 until (words.length - 1)) { + val result = ad.removeFirst + val expected = words(i) + assert(result == expected, + "deque(${i}): ${result} != expected: '${expected}'") + } + } + } + + test("size()") { + // exercised in ArrayDeque constructors + } + + test("toArray()") { + // exercised in ArrayDeque constructors + } + + test("toArray(null) - throws NullPointerException") { + val al1 = new ArrayDeque[String](Seq("apple", "banana", "cherry").asJava) + assertThrows[NullPointerException] { al1.toArray(null) } + } + + test("toArray(a: Array[T]) - arr is shorter") { + val al1 = new ArrayDeque[String](Seq("apple", "banana", "cherry").asJava) + val ain = Array.empty[String] + val aout = al1.toArray(ain) + assert(ain ne aout) + assert(Array("apple", "banana", "cherry") sameElements aout) + } + + test("toArray(a: Array[T]) - arr is the same length or longer") { + val al1 = new ArrayDeque[String](Seq("apple", "banana", "cherry").asJava) + val ain = Array.fill(4)("foo") + val aout = al1.toArray(ain) + assert(ain eq aout) + assert(Array("apple", "banana", "cherry", null) sameElements aout) + } + + test("toArray(Array[T]) - when T >: E") { + class SuperClass + class SubClass extends SuperClass + val in = Seq.fill(2)(new SubClass) + val al1 = new ArrayDeque[SubClass](in.asJava) + val aout = al1.toArray(Array.empty[SuperClass]) + assert(in.toArray sameElements aout) + } + + testFails("toArray(a: Array[T]) - throws ArrayStoreException when not " + + "T >: E", + 1694) { + class NotSuperClass + class SubClass + + locally { // This passes on Scala JVM + val ad = new ArrayList[SubClass]() + + ad.toArray(Array.empty[NotSuperClass]) + } + + locally { // This is the case which is failing on ScalaNative. + // The difference is that this Deque is not Empty. + val ad = new ArrayDeque(Seq(new SubClass).asJava) + + assertThrows[ArrayStoreException] { + ad.toArray(Array.empty[NotSuperClass]) + } + } + } +}