/*
* 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 com.google.j2objc;
import com.google.j2objc.annotations.AutoreleasePool;
import junit.framework.TestCase;
import java.util.Iterator;
import java.util.LinkedList;
/**
* Functional tests for correct memory behavior of LinkedList.
*
* @author Lukhnos Liu
*/
public class LinkedListTest extends TestCase {
// The default stack size is 8 MB on x64. Inside -dealloc, each call to another -dealloc takes
// 48 bytes, or 6 words (previous rbp, return addr, self, selector, and two additional words saved
// by the method). 8 MB / 48 bytes = 174762. The actual number to cause stack overflow is slightly
// lower as the stack is already being used for other things when the first -dealloc is called,
// but let's just use a bigger plus-one number here.
static final int SIZE_LARGE = 174763;
static final int SIZE_SMALL = 1000;
@AutoreleasePool
public void testLongList() {
LinkedList<Integer> list = new LinkedList<>();
insertIntoList(list, SIZE_LARGE);
assertEquals(SIZE_LARGE, list.size());
}
@AutoreleasePool
public void testLongListWithClear() {
LinkedList<Integer> list = new LinkedList<>();
insertIntoList(list, SIZE_LARGE);
list.clear();
assertTrue(list.isEmpty());
}
@AutoreleasePool
public void testLongListWithOneByOneRemoval() {
final int count = SIZE_LARGE;
LinkedList<Integer> list = new LinkedList<>();
insertIntoList(list, count);
for (int i = 0; i < count; i++) {
list.remove();
}
assertTrue(list.isEmpty());
}
@AutoreleasePool
public void testLongListWithIteratorRemoval() throws InterruptedException {
LinkedList<Integer> list = new LinkedList<>();
insertIntoList(list, SIZE_LARGE);
int previous = -1;
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
int next = it.next();
assertTrue(next - previous == 1);
previous = next;
it.remove();
}
assertTrue(list.isEmpty());
}
@AutoreleasePool
public void testLongListWithDescendingIteratorRemoval() {
final int count = SIZE_LARGE * 2;
LinkedList<Integer> list = new LinkedList<>();
insertIntoList(list, count);
int previous = count;
Iterator<Integer> it = list.descendingIterator();
// ReverseLinkIterator.remove() does not need the eager chain-breaking code like its forward
// counterpart LinkIterator.remove() does, and this is because if we remove a list in reverse,
// all removed nodes' next will point to the same node that was the next of the removed chain,
// and so when those removed nodes' -dealloc is called, the method will be releasing the same
// next_ node.
//
// We design this test case to remove from the middle, so that all removed nodes' next points to
// the first node in the second half of the list. This test would fail if this second half was
// not broken up properly during the clean up process.
for (int i = 0; i < count / 2; i++) {
assertTrue(it.hasNext());
it.next();
previous--;
}
while (it.hasNext()) {
int next = it.next();
assertTrue(previous - next == 1);
previous = next;
it.remove();
}
assertFalse(list.isEmpty());
}
@AutoreleasePool
public void testShortList() {
LinkedList<Integer> list = new LinkedList<>();
insertIntoList(list, SIZE_SMALL);
assertEquals(SIZE_SMALL, list.size());
}
void insertIntoList(LinkedList<Integer> list, int numElements) {
for (int i = 0; i < numElements; i++) {
list.add(i);
}
}
}