classlib: add CopyOnWriteArrayList implementation

This commit is contained in:
Alexey Andreev 2024-04-09 21:01:41 +02:00
parent 72b021fc0b
commit 9b41e3e814
2 changed files with 1365 additions and 0 deletions

View File

@ -0,0 +1,806 @@
/*
* Copyright 2024 Alexey Andreev.
*
* 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.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you 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 org.teavm.classlib.java.util.concurrent;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.RandomAccess;
import org.teavm.classlib.java.lang.TIndexOutOfBoundsException;
import org.teavm.classlib.java.util.TCollection;
import org.teavm.classlib.java.util.TIterator;
import org.teavm.classlib.java.util.TList;
import org.teavm.classlib.java.util.TListIterator;
public class TCopyOnWriteArrayList<E> implements TList<E>, RandomAccess, Cloneable, Serializable {
private volatile E[] array;
public TCopyOnWriteArrayList() {
array = newElementArray(0);
}
@SuppressWarnings("unchecked")
public TCopyOnWriteArrayList(Collection<? extends E> c) {
this.array = (E[]) c.toArray();
}
public TCopyOnWriteArrayList(E[] array) {
this.array = Arrays.copyOf(array, array.length);
}
@Override
public boolean add(E e) {
var copy = Arrays.copyOf(array, array.length + 1);
copy[array.length] = e;
array = copy;
return true;
}
@Override
public void add(int index, E e) {
checkIndexInclusive(index, array.length);
var copy = newElementArray(array.length + 1);
System.arraycopy(array, 0, copy, 0, index);
copy[index] = e;
System.arraycopy(array, index, copy, index + 1, array.length - index);
array = copy;
}
@Override
public boolean addAll(TCollection<? extends E> c) {
return addAll(size(), c);
}
@Override
public boolean addAll(int index, TCollection<? extends E> c) {
checkIndexInclusive(index, array.length);
if (c.isEmpty()) {
return false;
}
var copy = newElementArray(array.length + c.size());
System.arraycopy(array, 0, copy, 0, index);
var targetIndex = index;
var iter = c.iterator();
while (iter.hasNext()) {
copy[targetIndex++] = iter.next();
}
System.arraycopy(array, index, copy, targetIndex, array.length - index);
array = copy;
return true;
}
public int addAllAbsent(Collection<? extends E> c) {
if (c.isEmpty()) {
return 0;
}
var currentArray = array;
int count;
var toAdd = newElementArray(c.size());
repeat: do {
count = 0;
for (var o : c) {
if (indexOf(o) < 0) {
if (currentArray != array) {
currentArray = array;
continue repeat;
}
toAdd[count++] = o;
}
}
} while (false);
if (count > 0) {
var copy = newElementArray(array.length + count);
System.arraycopy(array, 0, copy, 0, array.length);
System.arraycopy(toAdd, 0, copy, array.length, count);
array = copy;
}
return count;
}
public boolean addIfAbsent(E e) {
if (!isEmpty() && indexOf(e) >= 0) {
return false;
}
add(e);
return true;
}
@Override
public void clear() {
if (!isEmpty()) {
array = newElementArray(0);
}
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("CloneNotSupportedException is not expected here");
}
}
@Override
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
@Override
public boolean containsAll(TCollection<?> c) {
return containsAll(c, array, 0, array.length);
}
private static boolean containsAll(TCollection<?> c, Object[] array, int start, int size) {
if (size == 0) {
return false;
}
var iter = c.iterator();
while (iter.hasNext()) {
if (indexOf(iter.next(), array, start, size) < 0) {
return false;
}
}
return true;
}
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof List)) {
return false;
}
var l = (List<?>) o;
var it = l.listIterator();
var ourIt = listIterator();
while (it.hasNext()) {
if (!ourIt.hasNext()) {
return false;
}
var thisListElem = it.next();
var anotherListElem = ourIt.next();
if (!(Objects.equals(thisListElem, anotherListElem))) {
return false;
}
}
return !ourIt.hasNext();
}
@Override
public E get(int index) {
return array[index];
}
public int hashCode() {
int hashCode = 1;
var it = listIterator();
while (it.hasNext()) {
var obj = it.next();
hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
}
return hashCode;
}
public int indexOf(E e, int index) {
if (index < 0) {
throw new IndexOutOfBoundsException();
}
return indexOf(e, array, index, array.length - index);
}
@SuppressWarnings("unchecked")
@Override
public int indexOf(Object o) {
return indexOf((E) o, 0);
}
@Override
public boolean isEmpty() {
return array.length == 0;
}
@Override
public TIterator<E> iterator() {
return listIterator();
}
public int lastIndexOf(E e, int index) {
if (index >= array.length) {
throw new IndexOutOfBoundsException();
}
return lastIndexOf(e, array, index, 0);
}
@SuppressWarnings("unchecked")
@Override
public int lastIndexOf(Object o) {
return lastIndexOf((E) o, size() - 1);
}
@Override
public TListIterator<E> listIterator() {
return new ListIteratorImpl<>(array, 0);
}
@Override
public TListIterator<E> listIterator(int index) {
checkIndexInclusive(index, array.length);
return new ListIteratorImpl<>(array, index);
}
@Override
public E remove(int index) {
return removeRange(index, 1);
}
@Override
public boolean remove(Object o) {
int index;
E[] currentArray;
do {
currentArray = array;
index = indexOf(o);
} while (currentArray != array);
if (index >= 0) {
remove(index);
return true;
}
return false;
}
@Override
public boolean removeAll(TCollection<?> c) {
return removeAll(c, 0, array.length) != 0;
}
@Override
public boolean retainAll(TCollection<?> c) {
Objects.requireNonNull(c);
return retainAll(c, 0, array.length) != 0;
}
@Override
public E set(int index, E e) {
int size = size();
checkIndexExclusive(index, size);
var copy = newElementArray(size);
System.arraycopy(array, 0, copy, 0, size);
var old = copy[index];
copy[index] = e;
array = copy;
return old;
}
@Override
public int size() {
return array.length;
}
@Override
public TList<E> subList(int fromIndex, int toIndex) {
return new SubList<>(this, fromIndex, toIndex);
}
@Override
public Object[] toArray() {
return toArray(array, 0, array.length);
}
@SuppressWarnings("unchecked")
@Override
public <T> T[] toArray(T[] a) {
return (T[]) toArray(a, array, 0, array.length);
}
@Override
public String toString() {
var sb = new StringBuilder("[");
var it = listIterator();
while (it.hasNext()) {
sb.append(it.next());
sb.append(", ");
}
if (sb.length() > 1) {
sb.setLength(sb.length() - 2);
}
sb.append("]");
return sb.toString();
}
@SuppressWarnings("unchecked")
private E[] newElementArray(int size) {
return (E[]) new Object[size];
}
private int removeAll(TCollection<?> c, int start, int size) {
if (c.isEmpty() || size == 0) {
return 0;
}
var currentArray = array;
var data = newElementArray(size);
int dataSize;
repeat: do {
dataSize = 0;
for (int i = start; i < (start + size); i++) {
if (!c.contains(array[i])) {
if (currentArray != array) {
currentArray = array;
continue repeat;
}
data[dataSize++] = array[i];
}
}
} while (false);
if (dataSize != size) {
var copy = newElementArray(array.length - (size - dataSize));
System.arraycopy(array, 0, copy, 0, start);
System.arraycopy(data, 0, copy, start, dataSize);
System.arraycopy(array, start + size, copy, start + dataSize, array.length
- (start + size));
array = copy;
return size - dataSize;
}
return 0;
}
private int retainAll(TCollection<?> c, int start, int size) {
if (size == 0) {
return 0;
}
if (c.isEmpty()) {
E[] copy;
if (size == array.length) {
copy = newElementArray(0);
} else {
copy = newElementArray(array.length - size);
System.arraycopy(array, 0, copy, 0, start);
System.arraycopy(array, start + size, copy, start, array.length - start - size);
}
array = copy;
return size;
}
var temp = newElementArray(size);
int pos;
var currentArray = array;
repeat: do {
pos = 0;
for (int i = start; i < (start + size); i++) {
if (c.contains(array[i])) {
if (array != currentArray) {
currentArray = array;
continue repeat;
}
temp[pos++] = array[i];
}
}
} while (false);
if (pos == size) {
return 0;
}
var copy = newElementArray(pos + array.length - size);
System.arraycopy(array, 0, copy, 0, start);
System.arraycopy(temp, 0, copy, start, pos);
System.arraycopy(array, start + size, copy, start + pos, array.length - start - size);
array = copy;
return size - pos;
}
private E removeRange(int start, int size) {
int sizeArr = size();
checkIndexExclusive(start, sizeArr);
checkIndexInclusive(start + size, sizeArr);
var copy = newElementArray(sizeArr - size);
System.arraycopy(array, 0, copy, 0, start);
var old = array[start];
if (sizeArr > (start + size)) {
System.arraycopy(array, start + size, copy, start, sizeArr - (start + size));
}
array = copy;
return old;
}
private static Object[] toArray(Object[] data, int start, int size) {
var result = new Object[size];
System.arraycopy(data, start, result, 0, size);
return result;
}
private static Object[] toArray(Object[] to, Object[] data, int start, int size) {
int l = data.length;
if (to.length < l) {
to = (Object[]) Array.newInstance(to.getClass().getComponentType(), l);
} else {
if (to.length > l) {
to[l] = null;
}
}
System.arraycopy(data, start, to, 0, size);
return to;
}
private static int lastIndexOf(Object o, Object[] data, int index, int limit) {
if (o != null) {
while (index >= limit) {
if (o.equals(data[index])) {
return index;
}
--index;
}
} else {
while (index >= limit) {
if (data[index] == null) {
return index;
}
--index;
}
}
return -1;
}
private static int indexOf(Object o, Object[] data, int start, int size) {
if (size == 0) {
return -1;
}
if (o == null) {
for (int i = start; i < start + size; i++) {
if (data[i] == null) {
return i;
}
}
} else {
for (int i = start; i < start + size; i++) {
if (o.equals(data[i])) {
return i;
}
}
}
return -1;
}
private static void checkIndexInclusive(int index, int size) {
if (index < 0 || index > size) {
throw new TIndexOutOfBoundsException();
}
}
private static void checkIndexExclusive(int index, int size) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index is " + index + ", size is " + size);
}
}
private static class ListIteratorImpl<E> implements TListIterator<E> {
private final E[] arr;
private int current;
private final int size;
ListIteratorImpl(E[] data, int current) {
this.current = current;
arr = data;
size = data.length;
}
@Override
public void add(Object o) {
throw new UnsupportedOperationException("Unsupported operation add");
}
@Override
public boolean hasNext() {
return current < size;
}
@Override
public boolean hasPrevious() {
return current > 0;
}
@Override
public E next() {
if (hasNext()) {
return arr[current++];
}
throw new NoSuchElementException("pos is " + current + ", size is " + size);
}
@Override
public int nextIndex() {
return current;
}
@Override
public E previous() {
if (hasPrevious()) {
return arr[--current];
}
throw new NoSuchElementException("pos is " + (current - 1) + ", size is " + size);
}
@Override
public int previousIndex() {
return current - 1;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Unsupported operation remove");
}
@Override
public void set(Object o) {
throw new UnsupportedOperationException("Unsupported operation set");
}
}
private static class SubList<E> implements TList<E> {
private final TCopyOnWriteArrayList<E> list;
private E[] data;
private int size;
private final int start;
SubList(TCopyOnWriteArrayList<E> list, int fromIdx, int toIdx) {
this.list = list;
checkIndexExclusive(fromIdx, list.array.length);
checkIndexInclusive(toIdx, list.array.length);
size = toIdx - fromIdx;
data = list.array;
start = fromIdx;
}
private void checkModifications() {
if (data != list.array) {
throw new ConcurrentModificationException();
}
}
@Override
public TListIterator<E> listIterator(int startIdx) {
return new SubListIterator(startIdx, data, size);
}
@Override
public E set(int index, E obj) {
checkIndexExclusive(index, size);
checkModifications();
var result = list.set(index + start, obj);
data = list.array;
return result;
}
@Override
public E get(int index) {
checkModifications();
checkIndexExclusive(index, size);
return data[index + start];
}
@Override
public int size() {
return size;
}
@Override
public E remove(int index) {
checkIndexExclusive(index, size);
checkModifications();
var obj = list.remove(index + start);
data = list.array;
size--;
return obj;
}
@Override
public void add(int index, E object) {
checkIndexInclusive(index, size);
checkModifications();
list.add(index + start, object);
data = list.array;
size++;
}
@Override
public boolean add(E o) {
checkModifications();
list.add(start + size, o);
data = list.array;
size++;
return true;
}
@Override
public boolean addAll(TCollection<? extends E> c) {
checkModifications();
int d = list.size();
list.addAll(start + size, c);
data = list.array;
size += list.size() - d;
return true;
}
@Override
public void clear() {
checkModifications();
list.removeRange(start, size);
data = list.array;
size = 0;
}
@Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}
@Override
public boolean containsAll(TCollection<?> c) {
return TCopyOnWriteArrayList.containsAll(c, data, start, size);
}
@Override
public int indexOf(Object o) {
int ind = TCopyOnWriteArrayList.indexOf(o, data, start, size);
return ind < 0 ? -1 : ind - start;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public TIterator<E> iterator() {
return new SubListIterator(0, data, size);
}
@Override
public int lastIndexOf(Object o) {
int ind = TCopyOnWriteArrayList.lastIndexOf(o, data, start + size - 1, start);
return ind < 0 ? -1 : ind - start;
}
@Override
public TListIterator<E> listIterator() {
return new SubListIterator(0, data, size);
}
@Override
public boolean remove(Object o) {
checkModifications();
int i;
Object[] currentArray;
do {
currentArray = data;
i = indexOf(o);
} while (currentArray != data);
if (i == -1) {
return false;
}
boolean result = list.remove(i + start) != null;
if (result) {
data = list.array;
size--;
}
return result;
}
@Override
public boolean removeAll(TCollection<?> c) {
checkModifications();
int removed = list.removeAll(c, start, size);
if (removed > 0) {
data = list.array;
size -= removed;
return true;
}
return false;
}
@Override
public boolean retainAll(TCollection<?> c) {
checkModifications();
int removed = list.retainAll(c, start, size);
if (removed > 0) {
data = list.array;
size -= removed;
return true;
}
return false;
}
@Override
public TList<E> subList(int fromIndex, int toIndex) {
return new SubList<>(list, start + fromIndex, start + toIndex);
}
@Override
public Object[] toArray() {
return TCopyOnWriteArrayList.toArray(data, start, size);
}
@SuppressWarnings("unchecked")
@Override
public <T> T[] toArray(T[] a) {
return (T[]) TCopyOnWriteArrayList.toArray(a, data, start, size);
}
@Override
public boolean addAll(int index, TCollection<? extends E> collection) {
checkIndexInclusive(index, size);
checkModifications();
int d = list.size();
boolean rt = list.addAll(index + start, collection);
data = list.array;
size += list.size() - d;
return rt;
}
private class SubListIterator extends ListIteratorImpl<E> {
int size;
private SubListIterator(int index, E[] data, int size) {
super(data, index + start);
this.size = size;
}
@Override
public int nextIndex() {
return super.nextIndex() - start;
}
@Override
public int previousIndex() {
return super.previousIndex() - start;
}
@Override
public boolean hasNext() {
return nextIndex() < size;
}
@Override
public boolean hasPrevious() {
return previousIndex() > -1;
}
}
}
}

View File

@ -0,0 +1,559 @@
/*
* Copyright 2024 Alexey Andreev.
*
* 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 org.teavm.classlib.java.util.concurrent;
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/licenses/publicdomain
* Other contributors include Andrew Wright, Jeffrey Hayes,
* Pat Fisher, Mike Judd.
*/
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.concurrent.CopyOnWriteArrayList;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
@RunWith(TeaVMTestRunner.class)
public class CopyOnWriteArrayListTest {
private static final int SIZE = 20;
private static CopyOnWriteArrayList<Object> populatedArray(int n) {
var a = new CopyOnWriteArrayList<>();
assertTrue(a.isEmpty());
for (int i = 0; i < n; ++i) {
a.add(i);
}
assertFalse(a.isEmpty());
assertEquals(n, a.size());
return a;
}
@Test
public void testConstructor() {
var a = new CopyOnWriteArrayList<>();
assertTrue(a.isEmpty());
}
@Test
public void testConstructor2() {
var ints = new Integer[SIZE];
for (int i = 0; i < SIZE - 1; ++i) {
ints[i] = i;
}
var a = new CopyOnWriteArrayList<>(ints);
for (int i = 0; i < SIZE; ++i) {
assertEquals(ints[i], a.get(i));
}
}
@Test
public void testConstructor3() {
var ints = new Integer[SIZE];
for (int i = 0; i < SIZE - 1; ++i) {
ints[i] = i;
}
var a = new CopyOnWriteArrayList<>(Arrays.asList(ints));
for (int i = 0; i < SIZE; ++i) {
assertEquals(ints[i], a.get(i));
}
}
@Test
public void testAddAll() {
var full = populatedArray(3);
var v = new ArrayList<>();
v.add(3);
v.add(4);
v.add(5);
full.addAll(v);
assertEquals(6, full.size());
}
@Test
public void testAddAllAbsent() {
var full = populatedArray(3);
var v = new ArrayList<>();
v.add(3);
v.add(4);
v.add(1); // will not add this element
full.addAllAbsent(v);
assertEquals(5, full.size());
}
@Test
public void testAddIfAbsent() {
var full = populatedArray(SIZE);
full.addIfAbsent(1);
assertEquals(SIZE, full.size());
}
@Test
public void testAddIfAbsent2() {
var full = populatedArray(SIZE);
full.addIfAbsent(3);
assertTrue(full.contains(3));
}
@Test
public void testClear() {
var full = populatedArray(SIZE);
full.clear();
assertEquals(0, full.size());
}
@Test
public void testClone() {
var l1 = populatedArray(SIZE);
@SuppressWarnings("unchecked")
var l2 = (CopyOnWriteArrayList<Object>) l1.clone();
assertEquals(l1, l2);
l1.clear();
assertNotEquals(l1, l2);
}
@Test
public void testContains() {
var full = populatedArray(3);
assertTrue(full.contains(1));
assertFalse(full.contains(5));
}
@Test
public void testAddIndex() {
var full = populatedArray(3);
full.add(0, -1);
assertEquals(4, full.size());
assertEquals(-1, full.get(0));
assertEquals(0, full.get(1));
full.add(2, -2);
assertEquals(5, full.size());
assertEquals(-2, full.get(2));
assertEquals(2, full.get(4));
}
@Test
public void testEquals() {
var a = populatedArray(3);
var b = populatedArray(3);
assertEquals(a, b);
assertEquals(b, a);
assertEquals(a.hashCode(), b.hashCode());
a.add(-1);
assertNotEquals(a, b);
assertNotEquals(b, a);
b.add(-1);
assertEquals(a, b);
assertEquals(b, a);
assertEquals(a.hashCode(), b.hashCode());
}
@Test
public void testContainsAll() {
var full = populatedArray(3);
var v = new ArrayList<>();
v.add(1);
v.add(2);
assertTrue(full.containsAll(v));
v.add(6);
assertFalse(full.containsAll(v));
}
@Test
public void testGet() {
var full = populatedArray(3);
assertEquals(0, ((Integer) full.get(0)).intValue());
}
@Test
public void testIndexOf() {
var full = populatedArray(3);
assertEquals(1, full.indexOf(1));
assertEquals(-1, full.indexOf("puppies"));
}
@Test
public void testIndexOf2() {
var full = populatedArray(3);
assertEquals(1, full.indexOf(1, 0));
assertEquals(-1, full.indexOf(1, 2));
}
@Test
public void testIsEmpty() {
var empty = new CopyOnWriteArrayList<>();
var full = populatedArray(SIZE);
assertTrue(empty.isEmpty());
assertFalse(full.isEmpty());
}
@Test
public void testIterator() {
var full = populatedArray(SIZE);
var i = full.iterator();
int j;
for (j = 0; i.hasNext(); j++) {
assertEquals(j, ((Integer) i.next()).intValue());
}
assertEquals(SIZE, j);
}
@Test
public void testIteratorRemove() {
var full = populatedArray(SIZE);
var it = full.iterator();
it.next();
try {
it.remove();
shouldThrow();
} catch (UnsupportedOperationException success) {
// do nothing
}
}
@Test
public void testToString() {
var full = populatedArray(3);
var s = full.toString();
for (int i = 0; i < 3; ++i) {
assertTrue(s.contains(String.valueOf(i)));
}
}
@Test
public void testLastIndexOf1() {
var full = populatedArray(3);
full.add(1);
full.add(3);
assertEquals(3, full.lastIndexOf(1));
assertEquals(-1, full.lastIndexOf(6));
}
@Test
public void testLastIndexOf2() {
var full = populatedArray(3);
full.add(1);
full.add(3);
assertEquals(3, full.lastIndexOf(1, 4));
assertEquals(-1, full.lastIndexOf(3, 3));
}
@Test
public void testListIterator1() {
var full = populatedArray(SIZE);
var i = full.listIterator();
int j;
for (j = 0; i.hasNext(); j++) {
assertEquals(j, ((Integer) i.next()).intValue());
}
assertEquals(SIZE, j);
}
@Test
public void testListIterator2() {
var full = populatedArray(3);
var i = full.listIterator(1);
int j;
for (j = 0; i.hasNext(); j++) {
assertEquals(j + 1, ((Integer) i.next()).intValue());
}
assertEquals(2, j);
}
@Test
public void testRemove() {
var full = populatedArray(3);
assertEquals(2, full.remove(2));
assertEquals(2, full.size());
}
@Test
public void testRemoveAll() {
var full = populatedArray(3);
var v = new ArrayList<>();
v.add(1);
v.add(2);
full.removeAll(v);
assertEquals(1, full.size());
}
@Test
public void testSet() {
var full = populatedArray(3);
assertEquals(2, full.set(2, 4));
assertEquals(4, ((Integer) full.get(2)).intValue());
}
@Test
public void testSize() {
var empty = new CopyOnWriteArrayList();
var full = populatedArray(SIZE);
assertEquals(SIZE, full.size());
assertEquals(0, empty.size());
}
@Test
public void testToArray() {
var full = populatedArray(3);
var o = full.toArray();
assertEquals(3, o.length);
assertEquals(0, ((Integer) o[0]).intValue());
assertEquals(1, ((Integer) o[1]).intValue());
assertEquals(2, ((Integer) o[2]).intValue());
}
@Test
public void testToArray2() {
var full = populatedArray(3);
var i = new Integer[3];
i = full.toArray(i);
assertEquals(3, i.length);
assertEquals(0, i[0].intValue());
assertEquals(1, i[1].intValue());
assertEquals(2, i[2].intValue());
}
@Test
public void testSubList() {
var a = populatedArray(10);
assertTrue(a.subList(1, 1).isEmpty());
for (int j = 0; j < 9; ++j) {
for (int i = j; i < 10; ++i) {
var b = a.subList(j, i);
for (int k = j; k < i; ++k) {
assertEquals(k, b.get(k - j));
}
}
}
var s = a.subList(2, 5);
assertEquals(s.size(), 3);
s.set(2, -1);
assertEquals(a.get(4), -1);
s.clear();
assertEquals(a.size(), 7);
}
@Test
@Ignore
public void testToArray_ArrayStoreException() {
try {
var c = new CopyOnWriteArrayList<>();
c.add("zfasdfsdf");
c.add("asdadasd");
c.toArray(new Long[5]);
shouldThrow();
} catch (ArrayStoreException e) {
// expected
}
}
@Test
public void testGet1_IndexOutOfBoundsException() {
try {
var c = new CopyOnWriteArrayList<>();
c.get(-1);
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testGet2_IndexOutOfBoundsException() {
try {
var c = new CopyOnWriteArrayList<>();
c.add("asdasd");
c.add("asdad");
c.get(100);
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testSet1_IndexOutOfBoundsException() {
try {
var c = new CopyOnWriteArrayList<>();
c.set(-1, "qwerty");
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testSet2() {
try {
var c = new CopyOnWriteArrayList<>();
c.add("asdasd");
c.add("asdad");
c.set(100, "qwerty");
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testAdd1_IndexOutOfBoundsException() {
try {
var c = new CopyOnWriteArrayList<>();
c.add(-1, "qwerty");
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testAdd2_IndexOutOfBoundsException() {
try {
var c = new CopyOnWriteArrayList<>();
c.add("asdasd");
c.add("asdasdasd");
c.add(100, "qwerty");
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testRemove1_IndexOutOfBounds() {
try {
var c = new CopyOnWriteArrayList<>();
c.remove(-1);
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testRemove2_IndexOutOfBounds() {
try {
var c = new CopyOnWriteArrayList<>();
c.add("asdasd");
c.add("adasdasd");
c.remove(100);
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testAddAll1_IndexOutOfBoundsException() {
try {
var c = new CopyOnWriteArrayList<>();
c.addAll(-1, new LinkedList<>());
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testAddAll2_IndexOutOfBoundsException() {
try {
var c = new CopyOnWriteArrayList<>();
c.add("asdasd");
c.add("asdasdasd");
c.addAll(100, new LinkedList<>());
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testListIterator1_IndexOutOfBoundsException() {
try {
var c = new CopyOnWriteArrayList<>();
c.listIterator(-1);
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testListIterator2_IndexOutOfBoundsException() {
try {
var c = new CopyOnWriteArrayList<>();
c.add("adasd");
c.add("asdasdas");
c.listIterator(100);
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testSubList1_IndexOutOfBoundsException() {
try {
var c = new CopyOnWriteArrayList<>();
c.subList(-1, 100);
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testSubList2_IndexOutOfBoundsException() {
try {
var c = new CopyOnWriteArrayList<>();
c.add("asdasd");
c.subList(1, 100);
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testSubList3_IndexOutOfBoundsException() {
try {
var c = new CopyOnWriteArrayList<>();
c.subList(3, 1);
shouldThrow();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
/**
* fail with message "should throw exception"
*/
private void shouldThrow() {
fail("Should throw exception");
}
}