/* * Copyright 2008 Google Inc. * * 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.gwt.emultest.java.util; import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.ListIterator; /** * Tests {@link Arrays}. */ public class ArraysTest extends EmulTestBase { /** * Helper class to use in sorted objects test. */ private static class TestObject { private static int count = 0; private int index; private int value; public TestObject(int value) { this.value = value; index = count++; } public int getIndex() { return index; } public int getValue() { return value; } public void setIndex(int val) { index = val; } @Override public String toString() { return value + "@" + index; } } @Override public String getModuleName() { return "com.google.gwt.emultest.EmulSuite"; } /** * Verifies that calling Arrays.hashCode(Object[]) with an array with * embedded null references works properly (and most importantly doesn't * throw an NPE). */ public void testArraysHashCodeWithNullElements() { String[] a = new String[] { "foo", null, "bar", "baz" }; Arrays.hashCode(a); } public void testArraysEqualsWithEmptyArrays() { assertTrue(Arrays.equals(new String[0], new String[0])); } public void testArraysEqualsWithoutNullElementsEqual() { assertTrue(Arrays.equals( new String[] { "foo" }, new String[]{ "foo" })); } public void testArraysEqualsWithoutNullElementsNotEqual() { assertFalse(Arrays.equals( new String[] { "foo" }, new String[]{ "bar" })); } public void testArraysEqualsWithNullElementsEqual() { assertTrue(Arrays.equals(new String[2], new String[2])); } public void testArraysEqualsWithNullElementsNotEqual() { assertFalse(Arrays.equals(new String[2], new String[1])); } public void testArraysEqualsWithNullAndNonNullElementsEqual() { assertTrue(Arrays.equals( new String[]{ null, "foo", null, "bar" }, new String[]{ null, "foo", null, "bar" })); } public void testArraysEqualsWithNullAndNonNullElementsNotEqual() { assertFalse(Arrays.equals( new String[]{ null, "bar", null, "foo" }, new String[]{ null, "foo", null, "foo" })); } /** * Tests {@link Arrays#asList(Object[])}. */ @SuppressWarnings("unchecked") public void testAsList() { // 0 Object[] test = {}; List result = Arrays.asList(test); assertEquals(test, result); // n Object[] test2 = {0, 1, 2}; List result2 = Arrays.asList(test2); assertEquals(test2, result2); // 1 Object[] test3 = {"Hello"}; List result3 = Arrays.asList(test3); assertEquals(test3, result3); } /** * Tests if changes to the list created by {@link Arrays#asList(Object[])} are * reflected in the original array. */ @SuppressWarnings("unchecked") public void testAsListBacking() { Object[] test1 = {0, 1, 2}; List result1 = Arrays.asList(test1); test1[0] = 3; assertEquals(test1, result1); Object[] test2 = {"a", "b", "c"}; List result2 = Arrays.asList(test2); result2.set(2, "x"); assertEquals(test2, result2); } /** * Tests {@link Arrays#asList(Object[])}. */ public void testAsListIsFixed() { List<String> list = Arrays.asList("foo", "bar", "baz"); try { list.add("bal"); fail("Expected UnsupportedOperationException"); } catch (UnsupportedOperationException expected) { } try { list.remove(0); fail("Expected UnsupportedOperationException"); } catch (UnsupportedOperationException expected) { } try { list.clear(); fail("Expected UnsupportedOperationException"); } catch (UnsupportedOperationException expected) { } Iterator<String> it = list.iterator(); it.next(); try { it.remove(); fail("Expected UnsupportedOperationException"); } catch (UnsupportedOperationException expected) { } ListIterator<String> lit = list.listIterator(); lit.next(); try { lit.add("bal"); fail("Expected UnsupportedOperationException"); } catch (UnsupportedOperationException expected) { } assertEquals(3, list.size()); } /** * Test Arrays.binarySearch(byte[], byte). * * <pre> * Verify the following cases: * empty array * odd numbers of elements * even numbers of elements * not found value larger than all elements * not found value smaller than all elements * negative values * </pre> */ public void testBinarySearchByte() { byte[] a1 = {}; int ret = Arrays.binarySearch(a1, (byte) 0); assertEquals(-1, ret); byte[] a2 = {1, 7, 31}; ret = Arrays.binarySearch(a2, (byte) 3); assertEquals(-2, ret); ret = Arrays.binarySearch(a2, (byte) 31); assertEquals(2, ret); byte[] a3 = {-71, 0, 35, 36}; ret = Arrays.binarySearch(a3, (byte) 42); assertEquals(-5, ret); ret = Arrays.binarySearch(a3, (byte) -80); assertEquals(-1, ret); ret = Arrays.binarySearch(a3, (byte) -71); assertEquals(0, ret); } /** * Test Arrays.binarySearch(char[], char). * * <pre> * Verify the following cases: * empty array * odd numbers of elements * even numbers of elements * not found value larger than all elements * not found value smaller than all elements * </pre> */ public void testBinarySearchChar() { char[] a1 = {}; int ret = Arrays.binarySearch(a1, (char) 0); assertEquals(-1, ret); char[] a2 = {1, 7, 31}; ret = Arrays.binarySearch(a2, (char) 3); assertEquals(-2, ret); ret = Arrays.binarySearch(a2, (char) 31); assertEquals(2, ret); char[] a3 = {1, 2, 35, 36}; ret = Arrays.binarySearch(a3, (char) 42); assertEquals(-5, ret); ret = Arrays.binarySearch(a3, (char) 0); assertEquals(-1, ret); ret = Arrays.binarySearch(a3, (char) 1); assertEquals(0, ret); } /** * Test Arrays.binarySearch(double[], double). * * <pre> * Verify the following cases: * empty array * odd numbers of elements * even numbers of elements * not found value larger than all elements * not found value smaller than all elements * negative values * </pre> */ public void testBinarySearchDouble() { double[] a1 = {}; int ret = Arrays.binarySearch(a1, 0); assertEquals(-1, ret); double[] a2 = {1, 7, 31}; ret = Arrays.binarySearch(a2, 3); assertEquals(-2, ret); ret = Arrays.binarySearch(a2, 31); assertEquals(2, ret); double[] a3 = {-71, 0, 35, 36}; ret = Arrays.binarySearch(a3, 42); assertEquals(-5, ret); ret = Arrays.binarySearch(a3, -80); assertEquals(-1, ret); ret = Arrays.binarySearch(a3, -71); assertEquals(0, ret); } /** * Test Arrays.binarySearch(float[], float). * * <pre> * Verify the following cases: * empty array * odd numbers of elements * even numbers of elements * not found value larger than all elements * not found value smaller than all elements * negative values * </pre> */ public void testBinarySearchFloat() { float[] a1 = {}; int ret = Arrays.binarySearch(a1, 0); assertEquals(-1, ret); float[] a2 = {1, 7, 31}; ret = Arrays.binarySearch(a2, 3); assertEquals(-2, ret); ret = Arrays.binarySearch(a2, 31); assertEquals(2, ret); float[] a3 = {-71, 0, 35, 36}; ret = Arrays.binarySearch(a3, 42); assertEquals(-5, ret); ret = Arrays.binarySearch(a3, -80); assertEquals(-1, ret); ret = Arrays.binarySearch(a3, -71); assertEquals(0, ret); } /** * Test Arrays.binarySearch(int[], int). * * <pre> * Verify the following cases: * empty array * odd numbers of elements * even numbers of elements * not found value larger than all elements * not found value smaller than all elements * negative values * </pre> */ public void testBinarySearchInt() { int[] a1 = {}; int ret = Arrays.binarySearch(a1, 0); assertEquals(-1, ret); int[] a2 = {1, 7, 31}; ret = Arrays.binarySearch(a2, 3); assertEquals(-2, ret); ret = Arrays.binarySearch(a2, 31); assertEquals(2, ret); int[] a3 = {-71, 0, 35, 36}; ret = Arrays.binarySearch(a3, 42); assertEquals(-5, ret); ret = Arrays.binarySearch(a3, -80); assertEquals(-1, ret); ret = Arrays.binarySearch(a3, -71); assertEquals(0, ret); } /** * Test Arrays.binarySearch(long[], long). * * <pre> * Verify the following cases: * empty array * odd numbers of elements * even numbers of elements * not found value larger than all elements * not found value smaller than all elements * negative values * </pre> */ public void testBinarySearchLong() { long[] a1 = {}; int ret = Arrays.binarySearch(a1, 0L); assertEquals(-1, ret); long[] a2 = {1, 7, 31}; ret = Arrays.binarySearch(a2, 3L); assertEquals(-2, ret); ret = Arrays.binarySearch(a2, 31L); assertEquals(2, ret); long[] a3 = {-71, 0, 35, 36}; ret = Arrays.binarySearch(a3, 42L); assertEquals(-5, ret); ret = Arrays.binarySearch(a3, -80L); assertEquals(-1, ret); ret = Arrays.binarySearch(a3, -71L); assertEquals(0, ret); } /** * Test Arrays.binarySearch(Object[], Object). * * <pre> * Verify the following cases: * empty array * odd numbers of elements * even numbers of elements * not found value larger than all elements * not found value smaller than all elements * </pre> */ public void testBinarySearchObject() { Object[] a1 = {}; int ret = Arrays.binarySearch(a1, ""); assertEquals(-1, ret); Object[] a2 = {"a", "g", "y"}; ret = Arrays.binarySearch(a2, "c"); assertEquals(-2, ret); ret = Arrays.binarySearch(a2, "y"); assertEquals(2, ret); Object[] a3 = {"b", "c", "x", "y"}; ret = Arrays.binarySearch(a3, "z"); assertEquals(-5, ret); ret = Arrays.binarySearch(a3, "a"); assertEquals(-1, ret); ret = Arrays.binarySearch(a3, "b"); assertEquals(0, ret); } /** * Test Arrays.binarySearch(Object[], Object, Comparator). * * <pre> * Verify the following cases: * empty array * odd numbers of elements * even numbers of elements * not found value larger than all elements * not found value smaller than all elements * Comparator uses natural ordering as a default * </pre> */ @SuppressWarnings("unchecked") public void testBinarySearchObjectComparator() { Comparator inverseSort = new Comparator() { public int compare(Object o1, Object o2) { return ((Comparable) o2).compareTo(o1); } }; Object[] a1 = {}; int ret = Arrays.binarySearch(a1, "", inverseSort); assertEquals(-1, ret); Object[] a2 = {"y", "g", "a"}; ret = Arrays.binarySearch(a2, "c", inverseSort); assertEquals(-3, ret); ret = Arrays.binarySearch(a2, "a", inverseSort); assertEquals(2, ret); Object[] a3 = {"y", "x", "c", "b"}; ret = Arrays.binarySearch(a3, "a", inverseSort); assertEquals(-5, ret); ret = Arrays.binarySearch(a3, "z", inverseSort); assertEquals(-1, ret); ret = Arrays.binarySearch(a3, "y", inverseSort); assertEquals(0, ret); Object[] a4 = {"a", "b", "c", "d", "e"}; ret = Arrays.binarySearch(a4, "d", null); // should not NPE assertEquals(3, ret); } /** * Test Arrays.binarySearch(short[], short). * * <pre> * Verify the following cases: * empty array * odd numbers of elements * even numbers of elements * not found value larger than all elements * not found value smaller than all elements * negative values * </pre> */ public void testBinarySearchShort() { short[] a1 = {}; int ret = Arrays.binarySearch(a1, (short) 0); assertEquals(-1, ret); short[] a2 = {1, 7, 31}; ret = Arrays.binarySearch(a2, (short) 3); assertEquals(-2, ret); ret = Arrays.binarySearch(a2, (short) 31); assertEquals(2, ret); short[] a3 = {-71, 0, 35, 36}; ret = Arrays.binarySearch(a3, (short) 42); assertEquals(-5, ret); ret = Arrays.binarySearch(a3, (short) -80); assertEquals(-1, ret); ret = Arrays.binarySearch(a3, (short) -71); assertEquals(0, ret); } /** * Tests sorting of long primitives. */ public void testLongSort() { long[] x = {3, 11, 2, 1, 22, 3}; Arrays.sort(x); assertEquals(1, x[0]); assertEquals(2, x[1]); assertEquals(3, x[2]); assertEquals(3, x[3]); assertEquals(11, x[4]); assertEquals(22, x[5]); } /** * Verifies that values are sorted numerically rather than as strings. */ public void testNumericSort() { Integer[] x = {3, 11, 2, 1}; Arrays.sort(x); assertEquals(2, x[1].intValue()); assertEquals(11, x[3].intValue()); } /** * Tests sorting primitives. */ public void testPrimitiveSort() { int[] x = {3, 11, 2, 1, 22, 3}; Arrays.sort(x); assertEquals(1, x[0]); assertEquals(2, x[1]); assertEquals(3, x[2]); assertEquals(3, x[3]); assertEquals(11, x[4]); assertEquals(22, x[5]); } /** * Tests sorting a subrange of a primitive array. */ public void testPrimitiveSubrangeSort() { int[] x = {3, 11, 2, 1, 22, 3}; Arrays.sort(x, 1, 5); assertEquals(3, x[0]); assertEquals(1, x[1]); assertEquals(2, x[2]); assertEquals(11, x[3]); assertEquals(22, x[4]); assertEquals(3, x[5]); } /** * Tests simple use cases for {@link Arrays#sort(Object[])}. */ public void testSimpleSort() { // empty array Object[] test = {}; Arrays.sort(test); assertEquals(test.length, 0); // array with one element Integer[] test2 = {1}; Arrays.sort(test2); assertEquals(1, test2[0].intValue()); // multiple elements Number[] test3 = {3, 0, 2, 4, 1}; Arrays.sort(test3); for (int i = 0; i < test3.length; i++) { assertEquals(i, test3[i].intValue()); } } /** * Tests {@link Arrays#sort(Object[], Comparator)}. */ public void testSort() { Object[] x = {"c", "b", "b", "a"}; int hash = x[1].hashCode(); Arrays.sort(x); int hash2 = x[1].hashCode(); assertEquals(hash, hash2); Object[] sorted = {"a", "b", "b", "c"}; assertEquals(x, sorted); Comparator<Object> t = new Comparator<Object>() { @SuppressWarnings("unchecked") public int compare(Object o1, Object o2) { return ((Comparable<Object>) o2).compareTo(o1); } }; Arrays.sort(x, t); int hash3 = x[1].hashCode(); assertEquals(hash, hash3); Object[] reverseSorted = {"c", "b", "b", "a"}; assertEquals(x, reverseSorted); } /** * Verifies that equal values retain their original order. This is done by * trying all possible permutations of a small test array to make sure the * sort algorithm properly handles any ordering. * * The current test is 6 elements, so there are 6! = 720 possible orderings to * test. */ public void testStableSort() { TestObject[] origData = new TestObject[] { new TestObject(3), new TestObject(11), new TestObject(2), new TestObject(3), new TestObject(1), new TestObject(3), new TestObject(22)}; int[] permutation = new int[origData.length]; while (validPermutation(permutation, origData.length)) { TestObject[] permutedArray = getPermutation(origData, permutation); Arrays.sort(permutedArray, new Comparator<TestObject>() { public int compare(TestObject a, TestObject b) { return a.getValue() - b.getValue(); } }); for (int i = 1; i < permutedArray.length; ++i) { if (permutedArray[i - 1].getValue() > permutedArray[i].getValue() || (permutedArray[i - 1].getValue() == permutedArray[i].getValue() && permutedArray[i - 1].getIndex() > permutedArray[i].getIndex())) { String msg = "Permutation " + Arrays.toString(permutation) + ": " + Arrays.toString(permutedArray); permutedArray = getPermutation(origData, permutation); msg += " (orig: " + Arrays.toString(permutedArray) + ")"; fail(msg); } } nextPermutation(permutation); } } /** * Returns a permuted array given the original array and a permutation. The * permutation is an array of indices which select which possible source goes * into the output slot of the same position. Note that previously used * sources are not counted, so the first permutation of a three-element array * [a,b,c] is [0,0,0], which maps to [a,b,c]. [1,0,0] maps to [b,a,c] since * the range of index[1] is from 0-1 and excludes the value b since it has * already been chosen. The permutation array may be shorter than the source * array, in which case it is choosing any m elements out of n. * * Thus the range of index i is 0 <= permutation[i] < n-i where n is the * number of elements in the source array. * * @param origData original array to permute * @param permutation array of indices, as described above * @return permuted array */ private TestObject[] getPermutation(TestObject[] origData, int[] permutation) { TestObject[] array = new TestObject[permutation.length]; for (int i = 0; i < permutation.length; ++i) { int idx = permutation[i]; // adjust for source elements already used for (int j = i; j-- > 0;) { if (permutation[j] <= idx) { idx++; } } array[i] = origData[idx]; // update position in output array for stability test array[i].setIndex(i); } return array; } /** * Advance the permutation to the next value. It leaves the first index set to * -1 if the range has been exceeded. * * @param permutation array of indices -- see {@link #getPermutation} for * details. */ private void nextPermutation(int[] permutation) { for (int i = 0; i < permutation.length; ++i) { if (++permutation[i] < permutation.length - i) { return; } permutation[i] = 0; } permutation[0] = -1; } /** * Checks to see if this permutation is valid; ie, if all of the indices are * between 0 and n-i (see {@link #getPermutation} for details). * * @param permutations array of indices * @param n length of source array. * @return true if the permutation is valid */ private boolean validPermutation(int[] permutations, int n) { if (permutations[0] < 0) { return false; } for (int i = 0; i < permutations.length; ++i) { if (permutations[i] >= n - i) { return false; } } return true; } }