/* * Written by Josh Bloch of Google Inc. and released to the public domain, * as explained at http://creativecommons.org/publicdomain/zero/1.0/. */ import java.io.*; import java.util.*; /** * Interface-based Deque tester. This test currently makes three * assumptions about the implementation under test: * * 1) It has no size limitation. * 2) It implements Serializable. * 3) It has a conventional (Collection) constructor. * * All of these assumptions could be relaxed. */ public class DequeBash { static int seed = 7; static int nextTail = 0; static int nextHead = -1; static int size() { return nextTail - nextHead - 1; } static int random() { int r = seed; r ^= r << 13; // xorshift r ^= r >>> 17; return seed = r ^ (r << 5); } static int random(int bound) { return (random() & 0x7fffffff) % bound; } public static void main(String[] args) throws Exception { Class cls = Class.forName(args[0]); long startTime = System.nanoTime(); for (int j = 0; j < 200; ++j) { @SuppressWarnings("unchecked") Deque deque = (Deque) cls.newInstance(); nextTail = 0; nextHead = -1; mainTest(deque, (random() & ((1 << 20) - 1))); if (deque.isEmpty()) System.out.print(" "); if ((j % 10) == 9) System.out.print("."); } long now = System.nanoTime(); long elapsedTimeMillis = (now - startTime) / (1000L * 1000L); System.out.printf("\ntotal time %d ms\n", elapsedTimeMillis); } static void mainTest(Deque deque, int n) throws Exception { /* * Perform a random sequence of operations, keeping contiguous * sequence of integers on the deque. */ for (int i = 0; i < n; i++) { sizeTests(deque); randomOp(deque); // Test iterator occasionally if ((i & 1023) == 0) { testIter(deque); testDescendingIter(deque); } // Test serialization and copying if ((i & 4095) == 0) { testEqual(deque, serialClone(deque)); testEqual(deque, copyConstructorClone(deque)); } // Test fancy removal stuff once in a blue moon if ((i & 8191) == 0) testRemove(deque); } // Stupid tests for clear, toString deque.clear(); testEmpty(deque); Collection c = Arrays.asList(1, 2, 3); deque.addAll(c); if (!deque.toString().equals("[1, 2, 3]")) throw new Exception("Deque.toString(): " + deque.toString()); } static void testIter(Deque deque) throws Exception { int next = nextHead + 1; int count = 0; for (int j : deque) { if (j != next++) throw new Exception("Element "+ j + " != " + (next-1)); count++; } if (count != size()) throw new Exception("Count " + count + " != " + size()); } static void testDescendingIter(Deque deque) throws Exception { int next = deque.size() + nextHead; int count = 0; for (Iterator it = deque.descendingIterator(); it.hasNext();) { int j = it.next(); if (j != next--) throw new Exception("Element "+ j + " != " + (next-1)); count++; } if (count != size()) throw new Exception("Count " + count + " != " + size()); } static void sizeTests(Deque deque) throws Exception { if (deque.size() != size()) throw new Exception("Size: " + deque.size() + ", expecting " + size()); if (deque.isEmpty() != (size() == 0)) throw new Exception( "IsEmpty " + deque.isEmpty() + ", size " + size()); // Check head and tail if (size() == 0) testEmpty(deque); else nonEmptyTest(deque); } static void nonEmptyTest(Deque deque) throws Exception { if (deque.getFirst() != nextHead + 1) throw new Exception("getFirst got: " + deque.getFirst() + " expecting " + (nextHead + 1)); if (deque.element() != nextHead + 1) throw new Exception("element got: " + deque.element() + " expecting " + (nextHead + 1)); if (deque.peekFirst() != nextHead + 1) throw new Exception("peekFirst got: "+deque.peekFirst() + " expecting " + (nextHead + 1)); if (deque.peek() != nextHead + 1) throw new Exception("peek got: " + deque.peek() + " expecting " + (nextHead + 1)); if (deque.peekLast() != nextTail - 1) throw new Exception("peekLast got: " + deque.peekLast() + " expecting " + (nextTail - 1)); if (deque.getLast() != nextTail - 1) throw new Exception("getLast got: " + deque.getLast() + " expecting " + (nextTail - 1)); } static void randomOp(Deque deque) throws Exception { // Perform a random operation switch (random() & 3) { case 0: switch (random() & 3) { case 0: deque.addLast(nextTail++); break; case 1: deque.offerLast(nextTail++); break; case 2: deque.offer(nextTail++); break; case 3: deque.add(nextTail++); break; default: throw new Exception("How'd we get here"); } break; case 1: if (size() == 0) { int result = 666; boolean threw = false; try { switch (random(3)) { case 0: result = deque.removeFirst(); break; case 1: result = deque.remove(); break; case 2: result = deque.pop(); break; default: throw new Exception("How'd we get here"); } } catch (NoSuchElementException e) { threw = true; } if (!threw) throw new Exception("Remove-no exception: " + result); } else { // deque nonempty int result = -1; switch (random(5)) { case 0: result = deque.removeFirst(); break; case 1: result = deque.remove(); break; case 2: result = deque.pop(); break; case 3: result = deque.pollFirst(); break; case 4: result = deque.poll(); break; default: throw new Exception("How'd we get here"); } if (result != ++nextHead) throw new Exception( "Removed "+ result + " expecting "+(nextHead - 1)); } break; case 2: switch (random(3)) { case 0: deque.addFirst(nextHead--); break; case 1: deque.offerFirst(nextHead--); break; case 2: deque.push(nextHead--); break; default: throw new Exception("How'd we get here"); } break; case 3: if (size() == 0) { int result = -1; boolean threw = false; try { result = deque.removeLast(); } catch (NoSuchElementException e) { threw = true; } if (!threw) throw new Exception("Remove-no exception: " + result); } else { // deque nonempty int result = ((random() & 1) == 0 ? deque.removeLast() : deque.pollLast()); if (result != --nextTail) throw new Exception( "Removed "+ result + " expecting "+(nextTail + 1)); } break; default: throw new Exception("How'd we get here"); } } private static void testEqual(Deque d1, Deque d2) throws Exception { if (d1.size() != d2.size()) throw new Exception("Size " + d1.size() + " != " + d2.size()); Iterator it = d2.iterator(); for (int i : d1) { int j = it.next(); if (j != i) throw new Exception("Element " + j + " != " + i); } for (int i : d1) if (!d2.contains(i)) throw new Exception("d2 doesn't contain " + i); for (int i : d2) if (!d1.contains(i)) throw new Exception("d2 doesn't contain " + i); if (d1.contains(Integer.MIN_VALUE)) throw new Exception("d2 contains Integer.MIN_VALUE"); if (d2.contains(Integer.MIN_VALUE)) throw new Exception("d2 contains Integer.MIN_VALUE"); if (d1.contains(null)) throw new Exception("d1 contains null"); if (d2.contains(null)) throw new Exception("d2 contains null"); if (!d1.containsAll(d2)) throw new Exception("d1 doesn't contain all of d2"); if (!d2.containsAll(d1)) throw new Exception("d2 doesn't contain all of d1"); Collection c = Collections.singleton(Integer.MIN_VALUE); if (d1.containsAll(c)) throw new Exception("d1 contains all of {Integer.MIN_VALUE }"); if (d2.containsAll(c)) throw new Exception("d2 contains all of {Integer.MIN_VALUE }"); } @SuppressWarnings("unchecked") private static T copyConstructorClone(T o) throws Exception { return (T) o.getClass().getConstructor(Collection.class).newInstance(o); } @SuppressWarnings("unchecked") private static T serialClone(T o) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(o); oos.flush(); oos.close(); ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bin); return (T) ois.readObject(); } private static void testRemove(Deque deque) throws Exception { Deque copy = ((random() & 1) == 0) ? copyConstructorClone(deque) : serialClone(deque); int numRemoved = 0; for (Iterator it = copy.iterator(); it.hasNext(); ) { if ((it.next() & 1) == 0) { it.remove(); numRemoved++; } } if (copy.size() + numRemoved != size()) throw new Exception((copy.size() + numRemoved) + " != " + size()); for (int i : copy) if ((i & 1) == 0) throw new Exception("Even number still present: " + i); List elements = Arrays.asList(copy.toArray(new Integer[0])); Collections.shuffle(elements); for (int e : elements) { if (!copy.contains(e)) throw new Exception(e + " missing."); boolean removed = false; switch (random(3)) { case 0: removed = copy.remove(e); break; case 1: removed = copy.removeFirstOccurrence(e); break; case 2: removed = copy.removeLastOccurrence(e); break; default: throw new Exception("How'd we get here"); } if (!removed) throw new Exception(e + " not removed."); if (copy.contains(e)) throw new Exception(e + " present after removal."); } testEmpty(copy); copy = copyConstructorClone(deque); copy.retainAll(deque); testEqual(deque, copy); copy.removeAll(deque); testEmpty(copy); } static boolean checkedThrows; private static void testEmpty(Deque deque) throws Exception { if (!deque.isEmpty()) throw new Exception("Deque isn't empty"); if (deque.size() != 0) throw new Exception("Deque size isn't zero"); if (!(deque.pollFirst() == null)) throw new Exception("pollFirst lies"); if (!(deque.poll() == null)) throw new Exception("poll lies"); if (!(deque.peekFirst() == null)) throw new Exception("peekFirst lies"); if (!(deque.peek() == null)) throw new Exception("peek lies"); if (!(deque.pollLast() == null)) throw new Exception("pollLast lies"); if (!(deque.peekLast() == null)) throw new Exception("peekLast lies"); if (!checkedThrows) { checkedThrows = true; boolean threw = false; int result = 666; try { result = ((random() & 1) == 0 ? deque.getFirst() : deque.element()); } catch (NoSuchElementException e) { threw = true; } if (!threw) throw new Exception("getFirst-no exception: "+result); threw = false; try { result = deque.getLast(); } catch (NoSuchElementException e) { threw = true; } if (!threw) throw new Exception("getLast-no exception: "+result); } } }