/* * Copyright 2015 Terracotta, Inc., a Software AG company. * * 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.terracotta.offheapstore; import static org.hamcrest.number.OrderingComparison.greaterThan; import static org.terracotta.offheapstore.util.MemoryUnit.KILOBYTES; import static org.terracotta.offheapstore.util.Generator.*; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.junit.Assert; import org.junit.Test; import org.terracotta.offheapstore.buffersource.HeapBufferSource; import org.terracotta.offheapstore.buffersource.OffHeapBufferSource; import org.terracotta.offheapstore.concurrent.AbstractConcurrentOffHeapMap; import org.terracotta.offheapstore.exceptions.OversizeMappingException; import org.terracotta.offheapstore.jdk8.BiFunction; import org.terracotta.offheapstore.jdk8.Function; import org.terracotta.offheapstore.paging.PageSource; import org.terracotta.offheapstore.paging.UpfrontAllocatingPageSource; import org.terracotta.offheapstore.util.Generator; import java.util.Arrays; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.NoSuchElementException; import java.util.Random; import java.util.concurrent.ConcurrentMap; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.hamcrest.core.IsNull.nullValue; import static org.hamcrest.core.StringContains.containsString; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeThat; import static org.terracotta.offheapstore.MetadataTuple.metadataTuple; /** * * @author Chris Dennis */ public abstract class AbstractOffHeapMapIT { public final Generator generator; public AbstractOffHeapMapIT(Generator generator) { this.generator = generator; } protected abstract Map<SpecialInteger, SpecialInteger> createMap(Generator generator); protected abstract Map<Integer, byte[]> createOffHeapBufferMap(PageSource source); @Test public void testRemoveByHash() throws Exception { final int ENTRIES_COUNT = 1013; Map<SpecialInteger, SpecialInteger> map = createMap(generator); HashingMap<SpecialInteger, SpecialInteger> hashingMap = (HashingMap<SpecialInteger, SpecialInteger>) map; for (int i = 0; i < ENTRIES_COUNT; i++) { SpecialInteger generated = generator.generate(i); hashingMap.put(generated, generated); } Map<SpecialInteger, SpecialInteger> removed = hashingMap.removeAllWithHash(BadInteger.HASHCODE); assertThat(removed.size(), greaterThan(0)); assertThat(hashingMap.size(), is(ENTRIES_COUNT - removed.size())); for (int i = 0; i < ENTRIES_COUNT; i++) { SpecialInteger generated = generator.generate(i); if (removed.containsKey(generated)) { assertThat(hashingMap.get(generated), nullValue()); } else { assertThat(hashingMap.get(generated), is(generated)); } } } @Test public void testNpeBehaviors() { Map<SpecialInteger, SpecialInteger> map = createMap(generator); try { map.containsKey(null); fail("Expected NullPointerException"); } catch (NullPointerException e) { //expected } try { map.remove(null); fail("Expected NullPointerException"); } catch (NullPointerException e) { //expected } try { map.get(null); fail("Expected NullPointerException"); } catch (NullPointerException e) { //expected } try { map.put(null, generator.generate(1)); fail("Expected NullPointerException"); } catch (NullPointerException e) { //expected } try { map.putAll(Collections.<SpecialInteger, SpecialInteger>singletonMap(null, generator.generate(1))); fail("Expected NullPointerException"); } catch (NullPointerException e) { //expected } } @Test public final void testResizing() { Map<SpecialInteger, SpecialInteger> map = createMap(generator); for (int i = 0; i < 100; i++) { map.put(generator.generate(i), generator.generate(i)); for (int j = 0; j <= i; j++) { Assert.assertEquals(generator.generate(j), map.get(generator.generate(j))); } } Assert.assertEquals(100, map.size()); Assert.assertFalse(map.isEmpty()); for (int i1 = 0; i1 < 100; i1++) { Assert.assertEquals(generator.generate(i1), map.remove(generator.generate(i1))); for (int j1 = i1 + 1; j1 < 100; j1++) { Assert.assertEquals(generator.generate(j1), map.get(generator.generate(j1))); } } Assert.assertTrue(map.isEmpty()); } @Test public final void testEmptyMap() { testEmptyMap(generator, createMap(generator)); } @Test public final void testPopulatedMap() { long randomSeed = System.nanoTime(); System.err.println(this.getClass() + ".testPopulatedMap random seed = " + randomSeed); Random rndm = new Random(randomSeed); Map<SpecialInteger, SpecialInteger> map = createMap(generator); SpecialInteger keyA = generator.generate(rndm.nextInt()); SpecialInteger valueA = generator.generate(rndm.nextInt()); assertThat(map.put(keyA, valueA), nullValue()); assertThat(map.get(keyA), is(valueA)); assertTrue(map.containsKey(keyA)); assertTrue(map.keySet().contains(keyA)); assertTrue(map.containsValue(valueA)); assertTrue(map.values().contains(valueA)); assertFalse(map.isEmpty()); SpecialInteger keyB; do { keyB = generator.generate(rndm.nextInt()); } while (keyB.equals(keyA)); SpecialInteger valueB1 = generator.generate(rndm.nextInt()); assertThat(map.put(keyB, valueB1), nullValue()); assertThat(map.get(keyB), is(valueB1)); assertTrue(map.containsKey(keyB)); assertTrue(map.keySet().contains(keyB)); assertTrue(map.containsValue(valueB1)); assertTrue(map.values().contains(valueB1)); assertFalse(map.isEmpty()); SpecialInteger valueB2 = generator.generate(rndm.nextInt()); assertThat(map.put(keyB, valueB2), is(valueB1)); assertThat(map.get(keyB), is(valueB2)); assertTrue(map.containsKey(keyB)); assertTrue(map.keySet().contains(keyB)); assertTrue(map.containsValue(valueB2)); assertTrue(map.values().contains(valueB2)); assertFalse(map.isEmpty()); assertThat(map.size(), is(2)); assertThat(map.entrySet().size(), is(2)); assertThat(map.values().size(), is(2)); assertFalse(map.isEmpty()); assertFalse(map.entrySet().isEmpty()); assertFalse(map.values().isEmpty()); Set<SpecialInteger> keySet = map.keySet(); //keySet assertFalse(keySet.isEmpty()); assertThat(keySet.size(), is(2)); assertTrue(keySet.equals(keySet));; assertTrue(keySet.containsAll(keySet)); assertTrue(keySet.containsAll(Arrays.asList(keySet.toArray()))); assertTrue(keySet.containsAll(Arrays.asList(keySet.toArray(new SpecialInteger[0])))); assertTrue(keySet.containsAll(new ArrayList<SpecialInteger>())); //keySet::toArray assertThat(keySet.toArray().length, is(2)); assertSame(keySet.toArray().getClass(), Object[].class); { SpecialInteger[] a = keySet.toArray(new SpecialInteger[0]); assertThat(a.length, is(2)); int i = 0; for (SpecialInteger j : keySet) { Assert.assertEquals(j, a[i++]); } assertSame(a.getClass(), SpecialInteger[].class); } { SpecialInteger[] a = keySet.toArray(new SpecialInteger[1]); assertThat(a.length, is(2)); int i = 0; for (SpecialInteger j : keySet) { Assert.assertEquals(j, a[i++]); } assertSame(a.getClass(), SpecialInteger[].class); } { SpecialInteger[] a = keySet.toArray(new SpecialInteger[2]); assertThat(a.length, is(2)); int i = 0; for (SpecialInteger j : keySet) { Assert.assertEquals(j, a[i++]); } assertSame(a.getClass(), SpecialInteger[].class); } { SpecialInteger[] a = keySet.toArray(new SpecialInteger[3]); assertThat(a.length, is(3)); int i = 0; for (SpecialInteger j : keySet) { Assert.assertEquals(j, a[i++]); } assertThat(a[2], nullValue()); assertSame(a.getClass(), SpecialInteger[].class); } Set<Map.Entry<SpecialInteger, SpecialInteger>> entrySet = map.entrySet(); //entrySet assertFalse(entrySet.isEmpty()); assertThat(entrySet.size(), is(2)); assertTrue(entrySet.equals(entrySet));; assertTrue(entrySet.containsAll(entrySet)); assertTrue(entrySet.containsAll(Arrays.asList(entrySet.toArray()))); assertTrue(entrySet.containsAll(Arrays.asList(entrySet.toArray(new Map.Entry[0])))); assertTrue(entrySet.containsAll(new ArrayList<Map.Entry>())); //entrySet::toArray assertThat(entrySet.toArray().length, is(2)); assertSame(entrySet.toArray().getClass(), Object[].class); { Map.Entry[] a = entrySet.toArray(new Map.Entry[0]); assertThat(a.length, is(2)); int i = 0; for (Map.Entry<SpecialInteger, SpecialInteger> j : entrySet) { Assert.assertEquals(j, a[i++]); } assertSame(a.getClass(), Map.Entry[].class); } { Map.Entry[] a = entrySet.toArray(new Map.Entry[1]); assertThat(a.length, is(2)); int i = 0; for (Map.Entry<SpecialInteger, SpecialInteger> j : entrySet) { Assert.assertEquals(j, a[i++]); } assertSame(a.getClass(), Map.Entry[].class); } { Map.Entry[] a = entrySet.toArray(new Map.Entry[2]); assertThat(a.length, is(2)); int i = 0; for (Map.Entry<SpecialInteger, SpecialInteger> j : entrySet) { Assert.assertEquals(j, a[i++]); } assertSame(a.getClass(), Map.Entry[].class); } { Map.Entry[] a = entrySet.toArray(new Map.Entry[3]); assertThat(a.length, is(3)); int i = 0; for (Map.Entry<SpecialInteger, SpecialInteger> j : entrySet) { Assert.assertEquals(j, a[i++]); } assertThat(a[2], nullValue()); assertSame(a.getClass(), Map.Entry[].class); } Collection<SpecialInteger> values = map.values(); //values assertFalse(values.isEmpty()); assertThat(values.size(), is(2)); assertTrue(values.containsAll(values)); assertTrue(values.containsAll(Arrays.asList(values.toArray()))); assertTrue(values.containsAll(Arrays.asList(values.toArray(new SpecialInteger[0])))); assertTrue(values.containsAll(new ArrayList<SpecialInteger>())); //values::toArray assertThat(values.toArray().length, is(2)); assertSame(values.toArray().getClass(), Object[].class); { SpecialInteger[] a = values.toArray(new SpecialInteger[0]); assertThat(a.length, is(2)); int i = 0; for (SpecialInteger j : values) { Assert.assertEquals(j, a[i++]); } assertSame(a.getClass(), SpecialInteger[].class); } { SpecialInteger[] a = values.toArray(new SpecialInteger[1]); assertThat(a.length, is(2)); int i = 0; for (SpecialInteger j : values) { Assert.assertEquals(j, a[i++]); } assertSame(a.getClass(), SpecialInteger[].class); } { SpecialInteger[] a = values.toArray(new SpecialInteger[2]); assertThat(a.length, is(2)); int i = 0; for (SpecialInteger j : values) { Assert.assertEquals(j, a[i++]); } assertSame(a.getClass(), SpecialInteger[].class); } { SpecialInteger[] a = values.toArray(new SpecialInteger[3]); assertThat(a.length, is(3)); int i = 0; for (SpecialInteger j : values) { Assert.assertEquals(j, a[i++]); } assertThat(a[2], nullValue()); assertSame(a.getClass(), SpecialInteger[].class); } } @Test public final void testTerminalEntryIterator() { Map<SpecialInteger, SpecialInteger> map = createMap(generator); for (int i = 0; i < 20; i++) { map.put(generator.generate(i), generator.generate(i)); } final Iterator<?> it = map.entrySet().iterator(); try { while (true) { it.next(); } } catch (NoSuchElementException e) { } } @Test public final void testTerminalEncodingIterator() { Map<SpecialInteger, SpecialInteger> map = createMap(generator); assumeThat(map, instanceOf(OffHeapHashMap.class)); OffHeapHashMap<SpecialInteger, SpecialInteger> offheapMap = (OffHeapHashMap<SpecialInteger, SpecialInteger>) map; for (int i = 0; i < 20; i++) { offheapMap.put(generator.generate(i), generator.generate(i)); } Iterator<?> it = offheapMap.encodingSet().iterator(); try { while (true) { it.next(); } } catch (NoSuchElementException e) { } } @Test public final void testTerminalValueIterator() { Map<SpecialInteger, SpecialInteger> map = createMap(generator); for (int i = 0; i < 20; i++) { map.put(generator.generate(i), generator.generate(i)); } final Iterator<?> it = map.values().iterator(); try { while (true) { it.next(); } } catch (NoSuchElementException e) { } } @Test public final void testTerminalKeyIterator() { Map<SpecialInteger, SpecialInteger> map = createMap(generator); for (int i = 0; i < 20; i++) { map.put(generator.generate(i), generator.generate(i)); } final Iterator<?> it = map.keySet().iterator(); try { while (true) { it.next(); } } catch (NoSuchElementException e) { } } @Test public final void testDestroyFreesResources() { UpfrontAllocatingPageSource source = new UpfrontAllocatingPageSource(new OffHeapBufferSource(), 4 * 1024 * 1024, 4 * 1024 * 1024); Collection<Map<Integer, byte[]>> maps = new ArrayList<Map<Integer, byte[]>>(); int count = 0; while (true) { try { Map<Integer, byte[]> m = createOffHeapBufferMap(source); maps.add(m); for (int i = 0; i < 100; i++) { m.put(i, new byte[1024]); } count++; } catch (OversizeMappingException e) { break; } catch (IllegalArgumentException e) { break; } } Assert.assertTrue(count != 0); for (Map<?, ?> m : maps) { if (m instanceof OffHeapHashMap<?, ?>) { ((OffHeapHashMap<?, ?>) m).destroy(); } else if (m instanceof AbstractConcurrentOffHeapMap<?, ?>) { ((AbstractConcurrentOffHeapMap<?, ?>) m).destroy(); } else if (m instanceof Segment<?, ?>) { ((Segment<?, ?>) m).destroy(); } else { Assert.fail(); } } Assert.assertEquals(0, source.getAllocatedSize()); for (Map<Integer, byte[]> m : maps) { try { m.put(1, new byte[1024]); Assert.fail("Expected NullPointerException"); } catch (IllegalStateException e) { Assert.assertThat(e.getMessage(), containsString("destroyed")); } } for (int c = 0; c < count; c++) { Map<Integer, byte[]> m = createOffHeapBufferMap(source); for (int i = 0; i < 100; i++) { Assert.assertNull(m.put(i, new byte[1024])); } } } @Test public final void testDestroyedMapIsEmpty() { Map<SpecialInteger, SpecialInteger> m = createMap(generator); for (int i = 0; i < 100; i++) { m.put(generator.generate(i), generator.generate(i)); } if (m instanceof OffHeapHashMap<?, ?>) { ((OffHeapHashMap<?, ?>) m).destroy(); } else if (m instanceof AbstractConcurrentOffHeapMap<?, ?>) { ((AbstractConcurrentOffHeapMap<?, ?>) m).destroy(); } else if (m instanceof Segment<?, ?>) { ((Segment<?, ?>) m).destroy(); } else { Assert.fail(); } testEmptyMap(generator, m); } @Test public final void testDestroyedMapIsImmutable() { Map<SpecialInteger, SpecialInteger> m = createMap(generator); for (int i = 0; i < 100; i++) { m.put(generator.generate(i), generator.generate(i)); } if (m instanceof OffHeapHashMap<?, ?>) { ((OffHeapHashMap<?, ?>) m).destroy(); } else if (m instanceof AbstractConcurrentOffHeapMap<?, ?>) { ((AbstractConcurrentOffHeapMap<?, ?>) m).destroy(); } else if (m instanceof Segment<?, ?>) { ((Segment<?, ?>) m).destroy(); } else { Assert.fail(); } m.clear(); m.putAll(Collections.<SpecialInteger, SpecialInteger>emptyMap()); try { m.put(generator.generate(1), generator.generate(1)); Assert.fail("Expected IllegalStateException"); } catch (IllegalStateException e) { Assert.assertThat(e.getMessage(), containsString("destroyed")); } try { m.putAll(Collections.singletonMap(generator.generate(1), generator.generate(1))); Assert.fail("Expected IllegalStateException"); } catch (IllegalStateException e) { Assert.assertThat(e.getMessage(), containsString("destroyed")); } } @Test public final void testDestroyWithLiveKeySetIterator() { UpfrontAllocatingPageSource source = new UpfrontAllocatingPageSource(new OffHeapBufferSource(), 4 * 1024 * 1024, 4 * 1024 * 1024); Map<Integer, byte[]> m = createOffHeapBufferMap(source); for (int i = 0; i < 10; i++) { m.put(i, new byte[128]); } Iterator<Integer> it = m.keySet().iterator(); for (int i = 0; i < 5; i++) { Assert.assertTrue(it.hasNext()); Assert.assertNotNull(it.next()); } Assert.assertTrue(it.hasNext()); m.clear(); if (m instanceof OffHeapHashMap<?, ?>) { ((OffHeapHashMap<?, ?>) m).destroy(); } else if (m instanceof AbstractConcurrentOffHeapMap<?, ?>) { ((AbstractConcurrentOffHeapMap<?, ?>) m).destroy(); } else if (m instanceof Segment<?, ?>) { ((Segment<?, ?>) m).destroy(); } else { Assert.fail(); } Assert.assertEquals(0, source.getAllocatedSize()); try { m.put(1, new byte[1024]); Assert.fail("Expected IllegalStateException"); } catch (IllegalStateException e) { //expected } if (m instanceof ConcurrentMap<?, ?>) { Assert.assertTrue(it.hasNext()); Assert.assertNotNull(it.next()); Assert.assertFalse(it.hasNext()); try { it.next(); Assert.fail("Expected NoSuchElementException"); } catch (NoSuchElementException e) { //expected } } else { try { it.next(); Assert.fail("Expected ConcurrentModificationException"); } catch (ConcurrentModificationException e) { //expected } } } @Test public void testFillBehavior() { PageSource source = new UpfrontAllocatingPageSource(new HeapBufferSource(), KILOBYTES.toBytes(32), KILOBYTES.toBytes(32)); Map<Integer, byte[]> m = createOffHeapBufferMap(source); for (int i = 0; i < 100000; i++) { Set<Integer> keySetCopy = new HashSet<Integer>(m.keySet()); doFill(m, i, new byte[i % 1024]); if (m.containsKey(i)) { Assert.assertNotNull(m.get(i)); Assert.assertEquals(i % 1024, m.get(i).length); Assert.assertTrue(keySetCopy.add(i)); Assert.assertEquals(keySetCopy, m.keySet()); } else { Assert.assertNull(m.get(i)); Assert.assertEquals(keySetCopy, m.keySet()); break; } } } @Test public final void testSimpleMutationUsingComputes() { long randomSeed = System.nanoTime(); System.err.println(this.getClass() + ".testSimpleMutationUsingCompute random seed = " + randomSeed); Random rndm = new Random(randomSeed); Map<SpecialInteger, SpecialInteger> map = createMap(generator); SpecialInteger keyA = generator.generate(rndm.nextInt()); final SpecialInteger valueA1 = generator.generate(rndm.nextInt()); final SpecialInteger valueA2 = generator.generate(valueA1.value() + 1); assertThat(doComputeIfPresentWithMetadata(map, keyA, new BiFunction<SpecialInteger, MetadataTuple<SpecialInteger>, MetadataTuple<SpecialInteger>>() { @Override public MetadataTuple<SpecialInteger> apply(SpecialInteger t, MetadataTuple<SpecialInteger> u) { throw new AssertionError("Unexpected function invocation"); } }), nullValue()); assertThat(doComputeIfAbsentWithMetadata(map, keyA, new Function<SpecialInteger, MetadataTuple<SpecialInteger>>() { @Override public MetadataTuple<SpecialInteger> apply(SpecialInteger t) { return metadataTuple(valueA1, 0); } }), is(metadataTuple(valueA1, 0))); assertThat(doComputeIfPresentWithMetadata(map, keyA, new BiFunction<SpecialInteger, MetadataTuple<SpecialInteger>, MetadataTuple<SpecialInteger>>() { @Override public MetadataTuple<SpecialInteger> apply(SpecialInteger t, MetadataTuple<SpecialInteger> u) { return metadataTuple(generator.generate(u.value().value() + 1), 16); } }), is(metadataTuple(valueA2, 16))); assertThat(map.get(keyA), is(valueA2)); assertThat(map.size(), is(1)); SpecialInteger keyB; do { keyB = generator.generate(rndm.nextInt()); } while (keyB.equals(keyA)); final SpecialInteger valueB1 = generator.generate(rndm.nextInt()); assertThat(doComputeWithMetadata(map, keyB, new BiFunction<SpecialInteger, MetadataTuple<SpecialInteger>, MetadataTuple<SpecialInteger>>() { @Override public MetadataTuple<SpecialInteger> apply(SpecialInteger t, MetadataTuple<SpecialInteger> u) { assertThat(u, nullValue()); return metadataTuple(valueB1, 0); } }), is(metadataTuple(valueB1, 0))); assertThat(map.get(keyB), is(valueB1)); assertThat(map.get(keyA), is(valueA2)); assertThat(map.size(), is(2)); final SpecialInteger valueB2 = generator.generate(rndm.nextInt()); assertThat(doComputeWithMetadata(map, keyB, new BiFunction<SpecialInteger, MetadataTuple<SpecialInteger>, MetadataTuple<SpecialInteger>>() { @Override public MetadataTuple<SpecialInteger> apply(SpecialInteger t, MetadataTuple<SpecialInteger> u) { return metadataTuple(valueB2, 32); } }), is(metadataTuple(valueB2, 32))); assertThat(map.get(keyB), is(valueB2)); assertThat(map.get(keyA), is(valueA2)); assertThat(map.size(), is(2)); assertThat(doComputeWithMetadata(map, keyA, new BiFunction<SpecialInteger, MetadataTuple<SpecialInteger>, MetadataTuple<SpecialInteger>>() { @Override public MetadataTuple<SpecialInteger> apply(SpecialInteger t, MetadataTuple<SpecialInteger> u) { return null; } }), nullValue()); assertThat(map.get(keyB), is(valueB2)); assertThat(map.get(keyA), nullValue()); assertThat(map.size(), is(1)); assertThat(doComputeIfPresentWithMetadata(map, keyB, new BiFunction<SpecialInteger, MetadataTuple<SpecialInteger>, MetadataTuple<SpecialInteger>>() { @Override public MetadataTuple<SpecialInteger> apply(SpecialInteger t, MetadataTuple<SpecialInteger> u) { return null; } }), nullValue()); assertThat(map.get(keyB), nullValue()); assertThat(map.get(keyA), nullValue()); assertThat(map.size(), is(0)); assertThat(doComputeIfPresentWithMetadata(map, keyA, new BiFunction<SpecialInteger, MetadataTuple<SpecialInteger>, MetadataTuple<SpecialInteger>>() { @Override public MetadataTuple<SpecialInteger> apply(SpecialInteger t, MetadataTuple<SpecialInteger> u) { return null; } }), nullValue()); assertThat(map.get(keyB), nullValue()); assertThat(map.get(keyA), nullValue()); assertThat(map.size(), is(0)); } private static void testEmptyMap(Generator g, Map<SpecialInteger, SpecialInteger> m) { assertThat(m.size(), is(0)); assertThat(m.entrySet().size(), is(0)); assertThat(m.values().size(), is(0)); assertTrue(m.isEmpty()); assertTrue(m.entrySet().isEmpty()); assertTrue(m.values().isEmpty()); assertThat(m.toString(), is("{}")); assertFalse(m.containsValue(g.generate(1))); assertFalse(m.containsKey(g.generate(1))); Set<SpecialInteger> keySet = m.keySet(); //keySet assertTrue(keySet.isEmpty()); assertThat(keySet.size(), is(0)); assertThat(keySet.toString(), is("[]")); assertThat(keySet.hashCode(), is(0)); assertTrue(keySet.containsAll(keySet)); assertTrue(keySet.equals(Collections.emptySet())); assertTrue(Collections.emptySet().equals(keySet)); assertTrue(keySet.equals(keySet)); //keySet::iterator assertFalse(keySet.iterator().hasNext()); try { keySet.iterator().next(); fail("Expected NoSuchElementException"); } catch (NoSuchElementException e) { //expected } //keySet::toArray assertThat(keySet.toArray().length, is(0)); assertSame(keySet.toArray().getClass(), Object[].class); SpecialInteger[] emptyKeyArray = keySet.toArray(new SpecialInteger[0]); assertSame(emptyKeyArray.getClass(), SpecialInteger[].class); assertThat(emptyKeyArray.length, is(0)); SpecialInteger[] nullKeyArray = keySet.toArray(new SpecialInteger[1]); assertSame(nullKeyArray.getClass(), SpecialInteger[].class); assertThat(nullKeyArray.length, is(1)); assertThat(nullKeyArray[0], nullValue()); Set<Map.Entry<SpecialInteger, SpecialInteger>> entrySet = m.entrySet(); //entrySet assertTrue(entrySet.isEmpty()); assertThat(entrySet.size(), is(0)); assertThat(entrySet.toString(), is("[]")); assertThat(entrySet.hashCode(), is(0)); assertTrue(entrySet.containsAll(entrySet)); assertTrue(entrySet.equals(Collections.emptySet())); assertTrue(Collections.emptySet().equals(entrySet)); assertTrue(entrySet.equals(entrySet)); //entrySet::iterator assertFalse(entrySet.iterator().hasNext()); try { entrySet.iterator().next(); fail("Expected NoSuchElementException"); } catch (NoSuchElementException e) { //expected } //entrySet::toArray assertThat(entrySet.toArray().length, is(0)); assertSame(entrySet.toArray().getClass(), Object[].class); Map.Entry[] emptyEntryArray = entrySet.toArray(new Map.Entry[0]); assertSame(emptyEntryArray.getClass(), Map.Entry[].class); assertThat(emptyEntryArray.length, is(0)); Map.Entry[] nullEntryArray = entrySet.toArray(new Map.Entry[1]); assertSame(nullEntryArray.getClass(), Map.Entry[].class); assertThat(nullEntryArray.length, is(1)); assertThat(nullEntryArray[0], nullValue()); Collection<SpecialInteger> values = m.values(); //values assertTrue(values.isEmpty()); assertThat(values.size(), is(0)); assertThat(values.toString(), is("[]")); assertTrue(values.containsAll(values)); //values::iterator assertFalse(values.iterator().hasNext()); try { values.iterator().next(); fail("Expected NoSuchElementException"); } catch (NoSuchElementException e) { //expected } //values::toArray assertThat(values.toArray().length, is(0)); assertSame(values.toArray().getClass(), Object[].class); SpecialInteger[] emptyValuesArray = values.toArray(new SpecialInteger[0]); assertSame(emptyValuesArray.getClass(), SpecialInteger[].class); assertThat(emptyValuesArray.length, is(0)); SpecialInteger[] nullValuesArray = values.toArray(new SpecialInteger[1]); assertSame(nullValuesArray.getClass(), SpecialInteger[].class); assertThat(nullValuesArray.length, is(1)); assertThat(nullValuesArray[0], nullValue()); } public static <K, V> V doFill(Map<K, V> map, K key, V value) { if (map instanceof OffHeapHashMap<?, ?>) { return ((OffHeapHashMap<K, V>) map).fill(key, value); } else if (map instanceof Segment<?, ?>) { return ((Segment<K, V>) map).fill(key, value); } else if (map instanceof AbstractConcurrentOffHeapMap<?, ?>) { return ((AbstractConcurrentOffHeapMap<K, V>) map).fill(key, value); } else { throw new AssertionError("Unexpected type : " + map.getClass()); } } public static <K, V> MetadataTuple<V> doComputeWithMetadata(Map<K, V> map, K key, BiFunction<? super K, ? super MetadataTuple<V>, ? extends MetadataTuple<V>> function) { if (map instanceof OffHeapHashMap<?, ?>) { return ((OffHeapHashMap<K, V>) map).computeWithMetadata(key, function); } else if (map instanceof Segment<?, ?>) { return ((Segment<K, V>) map).computeWithMetadata(key, function); } else if (map instanceof AbstractConcurrentOffHeapMap<?, ?>) { return ((AbstractConcurrentOffHeapMap<K, V>) map).computeWithMetadata(key, function); } else { throw new AssertionError("Unexpected type : " + map.getClass()); } } public static <K, V> MetadataTuple<V> doComputeIfAbsentWithMetadata(Map<K, V> map, K key, Function<? super K, ? extends MetadataTuple<V>> function) { if (map instanceof OffHeapHashMap<?, ?>) { return ((OffHeapHashMap<K, V>) map).computeIfAbsentWithMetadata(key, function); } else if (map instanceof Segment<?, ?>) { return ((Segment<K, V>) map).computeIfAbsentWithMetadata(key, function); } else if (map instanceof AbstractConcurrentOffHeapMap<?, ?>) { return ((AbstractConcurrentOffHeapMap<K, V>) map).computeIfAbsentWithMetadata(key, function); } else { throw new AssertionError("Unexpected type : " + map.getClass()); } } public static <K, V> MetadataTuple<V> doComputeIfPresentWithMetadata(Map<K, V> map, K key, BiFunction<? super K, ? super MetadataTuple<V>, ? extends MetadataTuple<V>> function) { if (map instanceof OffHeapHashMap<?, ?>) { return ((OffHeapHashMap<K, V>) map).computeIfPresentWithMetadata(key, function); } else if (map instanceof Segment<?, ?>) { return ((Segment<K, V>) map).computeIfPresentWithMetadata(key, function); } else if (map instanceof AbstractConcurrentOffHeapMap<?, ?>) { return ((AbstractConcurrentOffHeapMap<K, V>) map).computeIfPresentWithMetadata(key, function); } else { throw new AssertionError("Unexpected type : " + map.getClass()); } } }