/* * * 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.bookkeeper.util.collections; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.bookkeeper.util.collections.ConcurrentLongLongPairHashMap.LongPair; import org.junit.Test; import com.google.common.collect.Lists; import com.google.common.collect.Maps; public class ConcurrentLongLongPairHashMapTest { @Test public void testConstructor() { try { new ConcurrentLongLongPairHashMap(0); fail("should have thrown exception"); } catch (IllegalArgumentException e) { // ok } try { new ConcurrentLongLongPairHashMap(16, 0); fail("should have thrown exception"); } catch (IllegalArgumentException e) { // ok } try { new ConcurrentLongLongPairHashMap(4, 8); fail("should have thrown exception"); } catch (IllegalArgumentException e) { // ok } } @Test public void simpleInsertions() { ConcurrentLongLongPairHashMap map = new ConcurrentLongLongPairHashMap(16); assertTrue(map.isEmpty()); assertTrue(map.put(1, 1, 11, 11)); assertFalse(map.isEmpty()); assertTrue(map.put(2, 2, 22, 22)); assertTrue(map.put(3, 3, 33, 33)); assertEquals(map.size(), 3); assertEquals(map.get(1, 1), new LongPair(11, 11)); assertEquals(map.size(), 3); assertTrue(map.remove(1, 1)); assertEquals(map.size(), 2); assertEquals(map.get(1, 1), null); assertEquals(map.get(5, 5), null); assertEquals(map.size(), 2); assertTrue(map.put(1, 1, 11, 11)); assertEquals(map.size(), 3); assertTrue(map.put(1, 1, 111, 111)); assertEquals(map.size(), 3); } @Test public void testRemove() { ConcurrentLongLongPairHashMap map = new ConcurrentLongLongPairHashMap(); assertTrue(map.isEmpty()); assertTrue(map.put(1, 1, 11, 11)); assertFalse(map.isEmpty()); assertFalse(map.remove(0, 0)); assertFalse(map.remove(1, 1, 111, 111)); assertFalse(map.isEmpty()); assertTrue(map.remove(1, 1, 11, 11)); assertTrue(map.isEmpty()); } @Test public void testNegativeUsedBucketCount() { ConcurrentLongLongPairHashMap map = new ConcurrentLongLongPairHashMap(16, 1); map.put(0, 0, 0, 0); assertEquals(1, map.getUsedBucketCount()); map.put(0, 0, 1, 1); assertEquals(1, map.getUsedBucketCount()); map.remove(0, 0); assertEquals(0, map.getUsedBucketCount()); map.remove(0, 0); assertEquals(0, map.getUsedBucketCount()); } @Test public void testRehashing() { int n = 16; ConcurrentLongLongPairHashMap map = new ConcurrentLongLongPairHashMap(n / 2, 1); assertEquals(map.capacity(), n); assertEquals(map.size(), 0); for (int i = 0; i < n; i++) { map.put(i, i, i, i); } assertEquals(map.capacity(), 2 * n); assertEquals(map.size(), n); } @Test public void testRehashingWithDeletes() { int n = 16; ConcurrentLongLongPairHashMap map = new ConcurrentLongLongPairHashMap(n / 2, 1); assertEquals(map.capacity(), n); assertEquals(map.size(), 0); for (int i = 0; i < n / 2; i++) { map.put(i, i, i, i); } for (int i = 0; i < n / 2; i++) { map.remove(i, i); } for (int i = n; i < (2 * n); i++) { map.put(i, i, i, i); } assertEquals(map.capacity(), 2 * n); assertEquals(map.size(), n); } @Test public void concurrentInsertions() throws Throwable { ConcurrentLongLongPairHashMap map = new ConcurrentLongLongPairHashMap(); ExecutorService executor = Executors.newCachedThreadPool(); final int nThreads = 16; final int N = 100_000; long value = 55; List<Future<?>> futures = new ArrayList<>(); for (int i = 0; i < nThreads; i++) { final int threadIdx = i; futures.add(executor.submit(() -> { Random random = new Random(); for (int j = 0; j < N; j++) { long key1 = Math.abs(random.nextLong()); // Ensure keys are uniques key1 -= key1 % (threadIdx + 1); long key2 = Math.abs(random.nextLong()); // Ensure keys are uniques key2 -= key2 % (threadIdx + 1); map.put(key1, key2, value, value); } })); } for (Future<?> future : futures) { future.get(); } assertEquals(map.size(), N * nThreads); executor.shutdown(); } @Test public void concurrentInsertionsAndReads() throws Throwable { ConcurrentLongLongPairHashMap map = new ConcurrentLongLongPairHashMap(); ExecutorService executor = Executors.newCachedThreadPool(); final int nThreads = 16; final int N = 100_000; final long value = 55; List<Future<?>> futures = new ArrayList<>(); for (int i = 0; i < nThreads; i++) { final int threadIdx = i; futures.add(executor.submit(() -> { Random random = new Random(); for (int j = 0; j < N; j++) { long key1 = Math.abs(random.nextLong()); // Ensure keys are uniques key1 -= key1 % (threadIdx + 1); long key2 = Math.abs(random.nextLong()); // Ensure keys are uniques key2 -= key2 % (threadIdx + 1); map.put(key1, key2, value, value); } })); } for (Future<?> future : futures) { future.get(); } assertEquals(map.size(), N * nThreads); executor.shutdown(); } @Test public void testIteration() { ConcurrentLongLongPairHashMap map = new ConcurrentLongLongPairHashMap(); assertEquals(map.keys(), Collections.emptyList()); assertEquals(map.values(), Collections.emptyList()); map.put(0, 0, 0, 0); assertEquals(map.keys(), Lists.newArrayList(new LongPair(0, 0))); assertEquals(map.values(), Lists.newArrayList(new LongPair(0, 0))); map.remove(0, 0); assertEquals(map.keys(), Collections.emptyList()); assertEquals(map.values(), Collections.emptyList()); map.put(0, 0, 0, 0); map.put(1, 1, 11, 11); map.put(2, 2, 22, 22); List<LongPair> keys = map.keys(); Collections.sort(keys); assertEquals(keys, Lists.newArrayList(new LongPair(0, 0), new LongPair(1, 1), new LongPair(2, 2))); List<LongPair> values = map.values(); Collections.sort(values); assertEquals(values, Lists.newArrayList(new LongPair(0, 0), new LongPair(11, 11), new LongPair(22, 22))); map.put(1, 1, 111, 111); keys = map.keys(); Collections.sort(keys); assertEquals(keys, Lists.newArrayList(new LongPair(0, 0), new LongPair(1, 1), new LongPair(2, 2))); values = map.values(); Collections.sort(values); assertEquals(values, Lists.newArrayList(new LongPair(0, 0), new LongPair(22, 22), new LongPair(111, 111))); map.clear(); assertTrue(map.isEmpty()); } @Test public void testPutIfAbsent() { ConcurrentLongLongPairHashMap map = new ConcurrentLongLongPairHashMap(); assertTrue(map.putIfAbsent(1, 1, 11, 11)); assertEquals(map.get(1, 1), new LongPair(11, 11)); assertFalse(map.putIfAbsent(1, 1, 111, 111)); assertEquals(map.get(1, 1), new LongPair(11, 11)); } @Test public void testIvalidKeys() { ConcurrentLongLongPairHashMap map = new ConcurrentLongLongPairHashMap(16, 1); try { map.put(-5, 3, 4, 4); fail("should have failed"); } catch (IllegalArgumentException e) { // ok } try { map.get(-1, 0); fail("should have failed"); } catch (IllegalArgumentException e) { // ok } try { map.containsKey(-1, 0); fail("should have failed"); } catch (IllegalArgumentException e) { // ok } try { map.putIfAbsent(-1, 1, 1, 1); fail("should have failed"); } catch (IllegalArgumentException e) { // ok } } @Test public void testAsMap() { ConcurrentLongLongPairHashMap lmap = new ConcurrentLongLongPairHashMap(16, 1); lmap.put(1, 1, 11, 11); lmap.put(2, 2, 22, 22); lmap.put(3, 3, 33, 33); Map<LongPair, LongPair> map = Maps.newTreeMap(); map.put(new LongPair(1, 1), new LongPair(11, 11)); map.put(new LongPair(2, 2), new LongPair(22, 22)); map.put(new LongPair(3, 3), new LongPair(33, 33)); assertEquals(map, lmap.asMap()); } }