/* * WorldEdit, a Minecraft world manipulation toolkit * Copyright (C) sk89q * Copyright (C) WorldEdit team and contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package com.sk89q.worldedit.util.collection; import org.junit.jupiter.api.DisplayName; @DisplayName("An ordered block map") class BlockMapTest { /* private static final Platform MOCKED_PLATFORM = mock(Platform.class); @BeforeAll static void setupFakePlatform() { when(MOCKED_PLATFORM.getRegistries()).thenReturn(new BundledRegistries() { }); when(MOCKED_PLATFORM.getCapabilities()).thenReturn( Stream.of(Capability.values()) .collect(Collectors.toMap(Function.identity(), __ -> Preference.NORMAL)) ); PlatformManager platformManager = WorldEdit.getInstance().getPlatformManager(); platformManager.register(MOCKED_PLATFORM); WorldEdit.getInstance().getEventBus().post(new PlatformsRegisteredEvent()); registerBlock("minecraft:air"); registerBlock("minecraft:oak_wood"); } @AfterAll static void tearDownFakePlatform() throws Exception { WorldEdit.getInstance().getPlatformManager().unregister(MOCKED_PLATFORM); Field map = Registry.class.getDeclaredField("map"); map.setAccessible(true); ((Map) map.get(BlockType.REGISTRY)).clear(); } private static void registerBlock(String id) { BlockType.REGISTRY.register(id, new BlockType(id)); } @Mock private BiFunction biFunction; @Mock private BiFunction mergeFunction; @Mock private BiConsumer biConsumer; private final BaseBlock air = checkNotNull(BlockTypes.AIR).getDefaultState().toBaseBlock(); private final BaseBlock oakWood = checkNotNull(BlockTypes.OAK_WOOD).getDefaultState().toBaseBlock(); private final BlockMap map = BlockMap.createForBaseBlock(); @BeforeEach void setUp() { MockitoAnnotations.initMocks(this); } @AfterEach void tearDown() { map.clear(); } @Test @DisplayName("throws ClassCastException if invalid argument to get") void throwsFromGetOnInvalidArgument() { assertThrows(ClassCastException.class, () -> map.get("")); } @Nested @DisplayName("when created") class WhenCreated { @Test @DisplayName("is empty") void isEmpty() { assertEquals(0, map.size()); } @Test @DisplayName("is equal to another empty map") void isEqualToEmptyMap() { assertEquals(ImmutableMap.of(), map); } @Test @DisplayName("has the same hashCode as another empty map") void isHashCodeEqualToEmptyMap() { assertEquals(ImmutableMap.of().hashCode(), map.hashCode()); } @Test @DisplayName("returns `null` from get") void returnsNullFromGet() { assertNull(map.get(BlockVector3.ZERO)); } @Test @DisplayName("contains no keys") void containsNoKeys() { assertEquals(0, map.keySet().size()); assertFalse(map.containsKey(BlockVector3.ZERO)); } @Test @DisplayName("contains no values") void containsNoValues() { assertEquals(0, map.values().size()); assertFalse(map.containsValue(air)); } @Test @DisplayName("contains no entries") void containsNoEntries() { assertEquals(0, map.entrySet().size()); } @Test @DisplayName("returns the default value from getOrDefault") void returnsDefaultFromGetOrDefault() { assertEquals(air, map.getOrDefault(BlockVector3.ZERO, air)); } @Test @DisplayName("never calls the forEach action") void neverCallsForEachAction() { map.forEach(biConsumer); verifyNoMoreInteractions(biConsumer); } @Test @DisplayName("never calls the replaceAll function") void neverCallsReplaceAllFunction() { map.replaceAll(biFunction); verifyNoMoreInteractions(biFunction); } @Test @DisplayName("inserts on putIfAbsent") void insertOnPutIfAbsent() { assertNull(map.putIfAbsent(BlockVector3.ZERO, air)); assertEquals(1, map.size()); assertEquals(air, map.get(BlockVector3.ZERO)); } @Test @DisplayName("remove(key) returns null") void removeKeyReturnsNull() { assertNull(map.remove(BlockVector3.ZERO)); } @Test @DisplayName("remove(key, value) returns false") void removeKeyValueReturnsFalse() { assertFalse(map.remove(BlockVector3.ZERO, air)); } @Test @DisplayName("does nothing on replace") void doesNothingOnReplace() { assertNull(map.replace(BlockVector3.ZERO, air)); assertEquals(0, map.size()); assertFalse(map.replace(BlockVector3.ZERO, null, air)); assertEquals(0, map.size()); } @Test @DisplayName("inserts on computeIfAbsent") void insertOnComputeIfAbsent() { assertEquals(air, map.computeIfAbsent(BlockVector3.ZERO, k -> air)); assertEquals(1, map.size()); assertEquals(air, map.get(BlockVector3.ZERO)); } @Test @DisplayName("inserts on compute") void insertOnCompute() { assertEquals(air, map.compute(BlockVector3.ZERO, (k, v) -> air)); assertEquals(1, map.size()); assertEquals(air, map.get(BlockVector3.ZERO)); } @Test @DisplayName("does nothing on computeIfPresent") void doesNothingOnComputeIfPresent() { assertNull(map.computeIfPresent(BlockVector3.ZERO, (k, v) -> air)); assertEquals(0, map.size()); } @Test @DisplayName("inserts on merge, without calling merge function") void insertsOnMerge() { assertEquals(air, map.merge(BlockVector3.ZERO, air, mergeFunction)); assertEquals(1, map.size()); assertEquals(air, map.get(BlockVector3.ZERO)); verifyNoMoreInteractions(mergeFunction); } } @Nested @DisplayName("after having an entry added") class AfterEntryAdded { // Note: This section of tests would really benefit from // being able to parameterize classes. It's not part of JUnit // yet though: https://github.com/junit-team/junit5/issues/878 @VariedVectors.Test @DisplayName("has a size of one") void hasSizeOne(BlockVector3 vec) { map.put(vec, air); assertEquals(1, map.size()); } @VariedVectors.Test @DisplayName("is equal to another map with the same entry") void isEqualToSimilarMap(BlockVector3 vec) { map.put(vec, air); assertEquals(ImmutableMap.of(vec, air), map); } @VariedVectors.Test(provideNonMatching = true) @DisplayName("is not equal to another map with a different key") void isNotEqualToDifferentKeyMap(BlockVector3 vec, BlockVector3 nonMatch) { map.put(vec, air); assertNotEquals(ImmutableMap.of(nonMatch, air), map); } @VariedVectors.Test @DisplayName("is not equal to another map with a different value") void isNotEqualToDifferentValueMap(BlockVector3 vec) { map.put(vec, air); assertNotEquals(ImmutableMap.of(vec, oakWood), map); } @VariedVectors.Test @DisplayName("is not equal to an empty map") void isNotEqualToEmptyMap(BlockVector3 vec) { map.put(vec, air); assertNotEquals(ImmutableMap.of(), map); } @VariedVectors.Test @DisplayName("has the same hashCode as another map with the same entry") void isHashCodeEqualToSimilarMap(BlockVector3 vec) { map.put(vec, air); assertEquals(ImmutableMap.of(vec, air).hashCode(), map.hashCode()); } @VariedVectors.Test(provideNonMatching = true) @DisplayName("has a different hashCode from another map with a different key") void isHashCodeNotEqualToDifferentKeyMap(BlockVector3 vec, BlockVector3 nonMatch) { assumeFalse(vec.hashCode() == nonMatch.hashCode(), "Vectors have equivalent hashCodes, maps will too."); map.put(vec, air); assertNotEquals(ImmutableMap.of(nonMatch, air).hashCode(), map.hashCode()); } @VariedVectors.Test @DisplayName("has a different hashCode from another map with a different value") void isHashCodeNotEqualToDifferentValueMap(BlockVector3 vec) { map.put(vec, air); assertNotEquals(ImmutableMap.of(vec, oakWood).hashCode(), map.hashCode()); } @VariedVectors.Test @DisplayName("has a different hashCode from an empty map") void isHashCodeNotEqualToEmptyMap(BlockVector3 vec) { map.put(vec, air); assertNotEquals(ImmutableMap.of().hashCode(), map.hashCode()); } @VariedVectors.Test @DisplayName("returns value from get") void returnsValueFromGet(BlockVector3 vec) { map.put(vec, air); assertEquals(air, map.get(vec)); } @VariedVectors.Test(provideNonMatching = true) @DisplayName("returns `null` from get with different key") void returnsValueFromGet(BlockVector3 vec, BlockVector3 nonMatch) { map.put(vec, air); assertNotEquals(air, map.get(nonMatch)); } @VariedVectors.Test @DisplayName("contains the key") void containsTheKey(BlockVector3 vec) { map.put(vec, air); assertEquals(1, map.keySet().size()); assertTrue(map.keySet().contains(vec)); assertTrue(map.containsKey(vec)); } @VariedVectors.Test @DisplayName("contains the value") void containsTheValue(BlockVector3 vec) { map.put(vec, air); assertEquals(1, map.values().size()); assertTrue(map.values().contains(air)); assertTrue(map.containsValue(air)); } @VariedVectors.Test @DisplayName("contains the entry") void containsTheEntry(BlockVector3 vec) { map.put(vec, air); assertEquals(1, map.entrySet().size()); assertEquals(new AbstractMap.SimpleImmutableEntry<>(vec, air), map.entrySet().iterator().next()); } @VariedVectors.Test @DisplayName("returns the provided value from getOrDefault") void returnsProvidedFromGetOrDefault(BlockVector3 vec) { map.put(vec, air); assertEquals(air, map.getOrDefault(vec, oakWood)); } @VariedVectors.Test(provideNonMatching = true) @DisplayName("returns the default value from getOrDefault with a different key") void returnsDefaultFromGetOrDefaultWrongKey(BlockVector3 vec, BlockVector3 nonMatch) { map.put(vec, air); assertEquals(oakWood, map.getOrDefault(nonMatch, oakWood)); } @VariedVectors.Test @DisplayName("calls the forEach action once") void neverCallsForEachAction(BlockVector3 vec) { map.put(vec, air); map.forEach(biConsumer); verify(biConsumer).accept(vec, air); verifyNoMoreInteractions(biConsumer); } @VariedVectors.Test @DisplayName("replaces value using replaceAll") void neverCallsReplaceAllFunction(BlockVector3 vec) { map.put(vec, air); map.replaceAll((v, b) -> oakWood); assertEquals(oakWood, map.get(vec)); } @VariedVectors.Test @DisplayName("does not insert on `putIfAbsent`") void noInsertOnPutIfAbsent(BlockVector3 vec) { map.put(vec, air); assertEquals(air, map.putIfAbsent(vec, oakWood)); assertEquals(1, map.size()); assertEquals(air, map.get(vec)); } @VariedVectors.Test(provideNonMatching = true) @DisplayName("inserts on `putIfAbsent` to a different key") void insertOnPutIfAbsentDifferentKey(BlockVector3 vec, BlockVector3 nonMatch) { map.put(vec, air); assertNull(map.putIfAbsent(nonMatch, oakWood)); assertEquals(2, map.size()); assertEquals(air, map.get(vec)); assertEquals(oakWood, map.get(nonMatch)); } @VariedVectors.Test @DisplayName("remove(key) returns the old value") void removeKeyReturnsOldValue(BlockVector3 vec) { map.put(vec, air); assertEquals(air, map.remove(vec)); assertEquals(0, map.size()); } @VariedVectors.Test @DisplayName("keySet().remove(key) removes the entry from the map") void keySetRemovePassesThrough(BlockVector3 vec) { map.put(vec, air); assertTrue(map.keySet().remove(vec)); assertEquals(0, map.size()); } @VariedVectors.Test @DisplayName("entrySet().iterator().remove() removes the entry from the map") void entrySetIteratorRemovePassesThrough(BlockVector3 vec) { map.put(vec, air); Iterator> iterator = map.entrySet().iterator(); assertTrue(iterator.hasNext()); Map.Entry entry = iterator.next(); assertEquals(entry.getKey(), vec); iterator.remove(); assertEquals(0, map.size()); } @VariedVectors.Test(provideNonMatching = true) @DisplayName("remove(nonMatch) returns null") void removeNonMatchingKeyReturnsNull(BlockVector3 vec, BlockVector3 nonMatch) { map.put(vec, air); assertNull(map.remove(nonMatch)); assertEquals(1, map.size()); } @VariedVectors.Test @DisplayName("remove(key, value) returns true") void removeKeyValueReturnsTrue(BlockVector3 vec) { map.put(vec, air); assertTrue(map.remove(vec, air)); assertEquals(0, map.size()); } @VariedVectors.Test @DisplayName("remove(key, value) returns false for wrong value") void removeKeyValueReturnsFalseWrongValue(BlockVector3 vec) { map.put(vec, air); assertFalse(map.remove(vec, oakWood)); assertEquals(1, map.size()); } @VariedVectors.Test @DisplayName("replaces value at key") void replacesValueAtKey(BlockVector3 vec) { map.put(vec, air); assertEquals(air, map.replace(vec, oakWood)); assertEquals(1, map.size()); assertEquals(oakWood, map.get(vec)); assertTrue(map.replace(vec, oakWood, air)); assertEquals(1, map.size()); assertEquals(air, map.get(vec)); } @VariedVectors.Test(provideNonMatching = true) @DisplayName("does not replace value at different key") void doesNotReplaceAtDifferentKey(BlockVector3 vec, BlockVector3 nonMatch) { map.put(vec, air); assertNull(map.replace(nonMatch, oakWood)); assertEquals(1, map.size()); assertEquals(air, map.get(vec)); assertFalse(map.replace(nonMatch, air, oakWood)); assertEquals(1, map.size()); assertEquals(air, map.get(vec)); } @VariedVectors.Test @DisplayName("does not insert on computeIfAbsent") void doesNotInsertComputeIfAbsent(BlockVector3 vec) { map.put(vec, air); assertEquals(air, map.computeIfAbsent(vec, k -> { assertEquals(vec, k); return oakWood; })); assertEquals(1, map.size()); assertEquals(air, map.get(vec)); } @VariedVectors.Test(provideNonMatching = true) @DisplayName("inserts on computeIfAbsent with different key") void insertsOnComputeIfAbsentDifferentKey(BlockVector3 vec, BlockVector3 nonMatch) { map.put(vec, air); assertEquals(oakWood, map.computeIfAbsent(nonMatch, k -> { assertEquals(nonMatch, k); return oakWood; })); assertEquals(2, map.size()); assertEquals(air, map.get(vec)); assertEquals(oakWood, map.get(nonMatch)); } @VariedVectors.Test @DisplayName("replaces on compute") void replaceOnCompute(BlockVector3 vec) { map.put(vec, air); assertEquals(oakWood, map.compute(vec, (k, v) -> { assertEquals(vec, k); assertEquals(air, v); return oakWood; })); assertEquals(1, map.size()); assertEquals(oakWood, map.get(vec)); assertNull(map.compute(vec, (k, v) -> null)); assertEquals(0, map.size()); } @VariedVectors.Test(provideNonMatching = true) @DisplayName("inserts on compute with different key") void insertOnComputeDifferentKey(BlockVector3 vec, BlockVector3 nonMatch) { map.put(vec, air); assertEquals(oakWood, map.compute(nonMatch, (k, v) -> { assertEquals(nonMatch, k); assertNull(v); return oakWood; })); assertEquals(2, map.size()); assertEquals(air, map.get(vec)); assertEquals(oakWood, map.get(nonMatch)); assertNull(map.compute(nonMatch, (k, v) -> null)); assertEquals(1, map.size()); assertEquals(air, map.get(vec)); } @VariedVectors.Test @DisplayName("replaces on computeIfPresent") void replacesOnComputeIfPresent(BlockVector3 vec) { map.put(vec, air); assertEquals(oakWood, map.computeIfPresent(vec, (k, v) -> { assertEquals(vec, k); assertEquals(air, v); return oakWood; })); assertEquals(1, map.size()); assertEquals(oakWood, map.get(vec)); assertNull(map.computeIfPresent(vec, (k, v) -> null)); assertEquals(0, map.size()); } @VariedVectors.Test @DisplayName("inserts on merge, with call to merge function") void insertsOnMerge(BlockVector3 vec) { map.put(vec, air); assertEquals(oakWood, map.merge(vec, oakWood, (o, n) -> { assertEquals(air, o); assertEquals(oakWood, n); return n; })); assertEquals(1, map.size()); assertEquals(oakWood, map.get(vec)); } } @Test @DisplayName("contains all inserted vectors") void containsAllInsertedVectors() { Set allVectors = new VariedVectorGenerator() .makeVectorsStream() .collect(Collectors.toSet()); for (BlockVector3 vec : allVectors) { map.put(vec, air); } assertEquals(allVectors.size(), map.size()); assertEquals(allVectors, map.keySet()); for (Map.Entry entry : map.entrySet()) { assertTrue(allVectors.contains(entry.getKey())); assertEquals(air, entry.getValue()); } } */ }