blob: 6f449ee35610badcb633b4866fa25738c649798e [file] [log] [blame]
/*
* 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.apache.sis.util.collection;
import java.util.Set;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import org.opengis.referencing.cs.AxisDirection;
import org.opengis.metadata.citation.OnLineFunction;
import static org.opengis.referencing.cs.AxisDirection.*;
// Test dependencies
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import org.apache.sis.test.TestCase;
/**
* Tests the {@link CodeListSet} implementation.
*
* @author Martin Desruisseaux (Geomatys)
*/
public final class CodeListSetTest extends TestCase {
/**
* Creates a new test case.
*/
public CodeListSetTest() {
}
/**
* Creates a new set filled with up to 4 axis directions.
* The directions are (NORTH, EAST, UP, FUTURE) in that order.
*
* @param n Number of code list to add.
*/
@SuppressWarnings("fallthrough")
private CodeListSet<AxisDirection> create(final int n) {
final CodeListSet<AxisDirection> c = new CodeListSet<>(AxisDirection.class);
assertTrue(c.isEmpty());
switch (n) {
default: throw new AssertionError(n);
case 4: assertTrue(c.add(FUTURE)); // Fallthrough everywhere.
case 3: assertTrue(c.add(UP));
case 2: assertTrue(c.add(EAST));
case 1: assertTrue(c.add(NORTH));
case 0: break;
}
assertEquals(n, c.size());
return c;
}
/**
* Creates a code list of another kind. The returned set contains a code list having
* the same ordinal value as {@link AxisDirection#NORTH}, so we can detect if the
* {@code SortedSet} confuses the code list types.
*/
private CodeListSet<OnLineFunction> createOtherKind() {
// For the validity of the tests, ordinal value must be the same.
assertEquals(NORTH.ordinal(), OnLineFunction.INFORMATION.ordinal());
final CodeListSet<OnLineFunction> c = new CodeListSet<>(OnLineFunction.class);
assertTrue(c.add(OnLineFunction.INFORMATION));
return c;
}
/**
* Tests the {@link CodeListSet#toArray()} method.
* This will indirectly tests the iterator.
*/
@Test
public void testToArray() {
final CodeListSet<AxisDirection> c = create(4);
assertArrayEquals(new Object[] {NORTH, EAST, UP, FUTURE}, c.toArray());
}
/**
* Tests the {@link CodeListSet#clear()} method.
*/
@Test
public void testClear() {
final CodeListSet<AxisDirection> c = create(2);
assertFalse(c.isEmpty());
c.clear();
assertTrue(c.isEmpty());
}
/**
* Tests the {@link CodeListSet#contains(Object)} method.
*/
@Test
public void testContains() {
final CodeListSet<AxisDirection> c = create(4);
assertTrue (c.contains(NORTH));
assertFalse(c.contains(SOUTH));
assertTrue (c.contains(FUTURE));
assertFalse(c.contains(PAST));
assertTrue (c.contains(EAST));
assertFalse(c.contains(WEST));
assertTrue (c.contains(UP));
assertFalse(c.contains(DOWN));
assertFalse(c.contains(null), "Should be null-safe.");
assertFalse(c.contains(OnLineFunction.INFORMATION), "Code list of other kind should not be included.");
}
/**
* Tests the {@link CodeListSet#remove(Object)} method.
*/
@Test
public void testRemove() {
final CodeListSet<AxisDirection> c = create(4);
assertFalse(c.remove(null), "Should be null-safe.");
assertFalse(c.remove(OnLineFunction.INFORMATION), "Code list of other kind should not be included.");
assertTrue (c.remove (NORTH));
assertFalse(c.remove (SOUTH));
assertFalse(c.contains(NORTH));
assertEquals(3, c.size());
assertTrue (c.remove (FUTURE));
assertFalse(c.contains(FUTURE));
assertFalse(c.remove (PAST));
assertEquals(2, c.size());
assertTrue (c.remove (EAST));
assertTrue (c.remove (UP));
assertTrue (c.isEmpty());
}
/**
* Tests the {@link CodeListSet#containsAll(Collection)} method.
*/
@Test
public void testContainsAll() {
final CodeListSet<AxisDirection> c = create(4);
final CodeListSet<AxisDirection> o = create(4);
assertTrue (c.containsAll(o));
assertTrue (o.remove(NORTH));
assertTrue (o.remove(FUTURE));
assertTrue (c.containsAll(o));
assertTrue (o.add(NORTH_EAST));
assertFalse(c.containsAll(o));
assertFalse(c.containsAll(createOtherKind()));
}
/**
* Tests the {@link CodeListSet#removeAll(Collection)} method.
*/
@Test
public void testRemoveAll() {
final CodeListSet<AxisDirection> c = create(4);
final CodeListSet<AxisDirection> o = create(2);
assertTrue(o.add(NORTH_EAST)); // Extra value shall be ignored.
assertFalse(c.removeAll(createOtherKind()));
assertTrue(c.removeAll(o));
assertArrayEquals(new Object[] {UP, FUTURE}, c.toArray());
assertFalse(c.removeAll(o)); // Invoking a second time should not make any difference.
assertEquals(2, c.size());
}
/**
* Tests the {@link CodeListSet#removeAll(Collection)} method.
*/
@Test
public void testRetainAll() {
final CodeListSet<AxisDirection> c = create(4);
final CodeListSet<AxisDirection> o = create(2);
assertTrue(o.add(NORTH_EAST)); // Extra value shall be ignored.
assertTrue(c.retainAll(o));
assertArrayEquals(new Object[] {NORTH, EAST}, c.toArray());
assertFalse(c.retainAll(o)); // Invoking a second time should not make any difference.
assertEquals(2, c.size());
assertTrue(c.retainAll(createOtherKind()));
assertTrue(c.isEmpty());
}
/**
* Tests the {@link CodeListSet#addAll(Collection)} method.
*/
@Test
public void testAddAll() {
final CodeListSet<AxisDirection> c = create(1);
final CodeListSet<AxisDirection> o = create(3);
assertTrue(c.add(NORTH_EAST));
assertTrue(c.addAll(o));
assertArrayEquals(new Object[] {NORTH, NORTH_EAST, EAST, UP}, c.toArray());
assertFalse(c.addAll(o)); // Invoking a second time should not make any difference.
}
/**
* Tests the creation of a set filled with with all known values.
*/
@Test
public void testFill() {
final CodeListSet<AxisDirection> c = new CodeListSet<>(AxisDirection.class, true);
assertTrue(c.size() >= 32, "Expect at least 32 elements as of GeoAPI 3.0.");
assertTrue(c.toString().startsWith("[AxisDirection.OTHER, AxisDirection.NORTH, "));
/*
* Testing the full array would be too long and may change in future GeoAPI version
* anyway. Actually the main interest of this test is to ensure that the toString()
* method doesn't throw an IndexOutOfBoundsException (as it would be the case if
* the constructor had set too many bits).
*/
}
/**
* Tests the various methods with a code list containing more than 64 elements.
*/
@Test
public void testLargeCodeList() {
final Set<LargeCodeList> main = new HashSet<>(Arrays.asList(LargeCodeList.values()));
assertTrue(main.size() > Long.SIZE, "This test requires more than 64 elements.");
final CodeListSet<LargeCodeList> c = new CodeListSet<>(LargeCodeList.class);
/*
* Copy all content from the `main` to the CodeListSet. This will indirectly
* test CodeListSet.add(E), through the AbstractSet.addAll(Collection) method.
*/
assertTrue(c.addAll(main));
assertEquals(main.size(), c.size());
assertEquals(main, c);
assertFalse(c.addAll(main)); // Invoking a second time should not make any difference.
/*
* Keep a copy of the set before we modify it.
*/
final CodeListSet<LargeCodeList> clone = c.clone();
assertNotSame(c, clone);
assertEquals(main, clone);
assertEquals(clone, new CodeListSet<>(LargeCodeList.class, true));
/*
* Tests contains(Object) and remove(Object). We also remove elements
* from the `main` set, then we verify that the result is the same.
*/
LargeCodeList lastRemoved = null;
final Random random = new Random();
do {
for (final Iterator<LargeCodeList> it=main.iterator(); it.hasNext();) {
final LargeCodeList code = it.next();
assertTrue(c.contains(code), code.name());
if (random.nextBoolean()) {
assertTrue (c.remove(code), code.name());
assertFalse(c.contains(code), code.name());
it.remove();
lastRemoved = code;
if (main.size() == 1) {
// Very unlikely, but let be safe since the tests
// after the look require at least one element.
break;
}
}
}
} while (lastRemoved == null);
assertEquals(main, c);
assertFalse(c.isEmpty());
/*
* Test containsAll(Collection) and removeAll(Collection).
*/
assertTrue (clone.containsAll(c)); // The original set shall contain the decimated set.
assertFalse(c.containsAll(clone)); // The decimated set cannot contain the original set.
assertTrue (clone.remove(lastRemoved)); // Original set minus one element.
assertTrue (c.add(lastRemoved)); // Add an element to be ignored by removeAll(…).
assertTrue (clone.removeAll(c)); // Remove all elements found in the decimated set.
assertTrue (Collections.disjoint(main, clone)); // Expect no common elements.
assertFalse(clone.removeAll(c)); // Invoking a second time should not make any difference.
/*
* Test retainAll(Collection).
*/
assertTrue(clone.add(lastRemoved)); // Add the element to be retained.
assertTrue(c.retainAll(clone));
assertEquals(Set.of(lastRemoved), c);
}
}