/* * The MIT License * * Copyright 2015 Ahseya. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.github.horrorho.liquiddonkey.util; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import junitparams.JUnitParamsRunner; import static junitparams.JUnitParamsRunner.$; import junitparams.Parameters; import static org.hamcrest.CoreMatchers.is; import org.junit.Test; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Rule; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; import org.slf4j.MarkerFactory; /** * BiMapSetTest. * * @author Ahseya */ @RunWith(JUnitParamsRunner.class) public class BiMapSetTest { public static final Logger logger = LoggerFactory.getLogger(BiMapSetTest.class); public static final Marker marker = MarkerFactory.getMarker("TEST"); public static int threads = 4; public static int maxEven = 1000; public static final long timeoutMs = 30000; public final static Map<String, Set<Integer>> numbers = new HashMap<>(); public final static Map<String, Set<Integer>> empty = new HashMap<>(); public final static Map<String, Set<Integer>> nullKey = new HashMap<>(); public final static Map<String, Set<Integer>> nullValue = new HashMap<>(); public final static Map<String, Set<Integer>> nullKeyValue = new HashMap<>(); public final static Map<String, Set<Integer>> nullMap = null; public final static Map<String, Set<Integer>> oneStringToNumber = new HashMap<>(); public final static Map<Integer, Set<String>> oneNumberToString = new HashMap<>(); @Rule public final ExpectedException exception = ExpectedException.none(); @Before public void setUp() { numbers.clear(); numbers.put("odd", set(1, 3, 5, 9)); numbers.put("even", set(0, 2, 4, 6)); numbers.put("square", set(4, 9)); numbers.put("zero", set(0)); numbers.put("hundred", set()); nullKey.clear(); nullKey.put("odd", set(1, 3, 5)); nullKey.put(null, set(2, 4, 6)); nullValue.clear(); nullValue.put("odd", set(1, null, 5)); nullValue.put("even", set(2, 4, 6)); nullKeyValue.clear(); nullKeyValue.put("odd", set(1, null, 5)); nullKeyValue.put(null, set(2, 4, 6)); oneStringToNumber.clear(); oneStringToNumber.put("odd", set(1, 3, 5, 9)); oneStringToNumber.put("even", set(0, 2, 4, 6)); oneStringToNumber.put("zero", set(0)); oneStringToNumber.put("square", set(4, 9)); oneNumberToString.clear(); oneNumberToString.put(0, set("zero", "even")); oneNumberToString.put(1, set("odd")); oneNumberToString.put(2, set("even")); oneNumberToString.put(3, set("odd")); oneNumberToString.put(4, set("even", "square")); oneNumberToString.put(5, set("odd")); oneNumberToString.put(6, set("even")); oneNumberToString.put(9, set("odd", "square")); } @Test public void testFrom() { BiMapSet<String, Integer> oneSet = BiMapSet.from(numbers); assertThat(oneSet.kToVSet, is(oneStringToNumber)); assertThat(oneSet.vToKSet, is(oneNumberToString)); } @Test @Parameters public void testFromException(Map map) { exception.expect(NullPointerException.class); BiMapSet.from(map); } public static Object[] parametersForTestFromException() { return new Object[]{nullMap, nullKey, nullValue, nullKeyValue}; } @Test public void testKeySet() { BiMapSet<String, Integer> oneSet = BiMapSet.from(numbers); assertThat(oneSet.keySet(), is(oneStringToNumber.keySet())); } @Test public void testValueSet() { BiMapSet<String, Integer> oneSet = BiMapSet.from(numbers); assertThat(oneSet.valueSet(), is(oneNumberToString.keySet())); } @Test @Parameters public void testKey(Map<String, Set<Integer>> map, Integer value, Set<String> keys) { BiMapSet<String, Integer> mapSet = BiMapSet.from(map); assertThat(mapSet.keys(value), is(keys)); } public static Object[] parametersForTestKey() { return new Object[]{ $(numbers, null, set()), $(numbers, -1, set()), $(numbers, 0, set("even", "zero")), $(numbers, 1, set("odd")), $(numbers, 2, set("even")), $(numbers, 3, set("odd")), $(numbers, 4, set("even", "square")), $(numbers, 5, set("odd")), $(numbers, 6, set("even")), $(numbers, 9, set("odd", "square")) }; } @Test @Parameters public void testValue(Map<String, Set<Integer>> map, String key, Set<Integer> values) { BiMapSet<String, Integer> mapSet = BiMapSet.from(map); assertThat(mapSet.values(key), is(values)); } public static Object[] parametersForTestValue() { return new Object[]{ $(numbers, null, set()), $(numbers, "", set()), $(numbers, "null", set()), $(numbers, "zero", set(0)), $(numbers, "odd", set(1, 3, 5, 9)), $(numbers, "even", set(0, 2, 4, 6)), $(numbers, "square", set(4, 9)), $(numbers, "hundred", set()) }; } @Test public void testRemoveKey() { BiMapSet<String, Integer> oneSet = BiMapSet.from(numbers); assertThat(oneSet.removeKey(null), is(set())); assertThat(oneSet.removeKey("null"), is(set())); assertThat(oneSet.removeKey("odd"), is(set(1, 3, 5))); assertThat(oneSet.removeKey("even"), is(set(2, 6))); assertThat(oneSet.removeKey("square"), is(set(4, 9))); assertThat(oneSet.removeKey("square"), is(set())); assertThat(oneSet.removeKey("zero"), is(set(0))); assertThat(oneSet.removeKey(null), is(set())); assertThat(oneSet.removeKey("null"), is(set())); } @Test public void testRemoveValue() { BiMapSet<String, Integer> oneSet = BiMapSet.from(numbers); assertThat(oneSet.removeValue(null), is(set())); assertThat(oneSet.removeValue(0), is(set("zero"))); assertThat(oneSet.removeValue(1), is(set())); assertThat(oneSet.removeValue(2), is(set())); assertThat(oneSet.removeValue(3), is(set())); assertThat(oneSet.removeValue(4), is(set())); assertThat(oneSet.removeValue(5), is(set())); assertThat(oneSet.removeValue(6), is(set("even"))); assertThat(oneSet.removeValue(9), is(set("odd", "square"))); } @Test public void testIsEmpty() { BiMapSet<String, Integer> oneSet = BiMapSet.from(numbers); assertThat(oneSet.isEmpty(), is(false)); BiMapSet<String, Integer> emptySet = BiMapSet.from(empty); assertThat(emptySet.isEmpty(), is(true)); } @Test public void testCompound() { BiMapSet<String, Integer> oneSet = BiMapSet.from(numbers); assertThat(oneSet.isEmpty(), is(false)); assertThat(oneSet.removeKey("null"), is(set())); assertThat(oneSet.isEmpty(), is(false)); assertThat(oneSet.removeKey(null), is(set())); assertThat(oneSet.isEmpty(), is(false)); assertThat(oneSet.removeValue(null), is(set())); assertThat(oneSet.isEmpty(), is(false)); assertThat(oneSet.removeValue(-1), is(set())); assertThat(oneSet.isEmpty(), is(false)); assertThat(oneSet.removeKey("odd"), is(set(1, 3, 5))); assertThat(oneSet.isEmpty(), is(false)); assertThat(oneSet.removeValue(0), is(set("zero"))); assertThat(oneSet.isEmpty(), is(false)); assertThat(oneSet.removeValue(4), is(set())); assertThat(oneSet.isEmpty(), is(false)); assertThat(oneSet.removeValue(9), is(set("square"))); assertThat(oneSet.isEmpty(), is(false)); assertThat(oneSet.removeKey("even"), is(set(2, 6))); assertThat(oneSet.isEmpty(), is(true)); } @Test public void testConcurrent() throws InterruptedException { Map<String, Set<Integer>> map = new HashMap<>(); map.put("number", new HashSet<>()); map.put("odd", new HashSet<>()); map.put("even", new HashSet<>()); map.put("seven", new HashSet<>()); Set<Integer> odd = new HashSet<>(); Set<Integer> seven = new HashSet<>(); for (int i = 0; i < maxEven; i++) { map.get("number").add(i); if (i % 2 == 0) { map.get("even").add(i); } else { map.get("odd").add(i); odd.add(i); if (i % 7 == 0) { map.get("seven").add(i); seven.add(i); } } } BiMapSet set = BiMapSet.from(map); ExecutorService executor = Executors.newCachedThreadPool(); AtomicInteger pending = new AtomicInteger(0); Supplier<Runnable> runnables = () -> runnable(pending, set, maxEven); Stream.generate(runnables).limit(threads).forEach(executor::submit); executor.shutdown(); executor.awaitTermination(timeoutMs, TimeUnit.MILLISECONDS); executor.shutdownNow(); assertThat(pending.get(), is(0)); assertThat(set.values("odd"), is(odd)); assertThat(set.values("seven"), is(seven)); assertThat(set.keySet(), is(set("odd", "number", "seven"))); } public Runnable runnable(AtomicInteger pending, BiMapSet<String, Integer> set, int max) { return () -> { logger.trace(marker, "<< runnable()"); try { pending.incrementAndGet(); for (int i = 0; i < max; i += 2) { set.removeValue(i); } } finally { pending.decrementAndGet(); logger.trace(marker, ">> runnable()"); } }; } public static <T> Set<T> set(T... t) { return Stream.of(t).collect(Collectors.toSet()); } }