/**
* 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.hadoop.hbase.types;
import org.apache.hadoop.hbase.testclassification.MiscTests;
import org.apache.hadoop.hbase.testclassification.SmallTests;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import java.util.Map;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ThreadLocalRandom;
import static org.junit.Assert.*;
@Category({MiscTests.class, SmallTests.class})
public class TestCopyOnWriteMaps {
private static final int MAX_RAND = 10 * 1000 * 1000;
private ConcurrentNavigableMap<Long, Long> m;
private ConcurrentSkipListMap<Long, Long> csm;
@Before
public void setUp() {
m = new CopyOnWriteArrayMap<>();
csm = new ConcurrentSkipListMap<>();
for ( long i = 0 ; i < 10000; i++ ) {
long o = ThreadLocalRandom.current().nextLong(MAX_RAND);
m.put(i, o);
csm.put(i,o);
}
long o = ThreadLocalRandom.current().nextLong(MAX_RAND);
m.put(0L, o);
csm.put(0L,o);
}
@Test
public void testSize() throws Exception {
assertEquals("Size should always be equal", m.size(), csm.size());
}
@Test
public void testIsEmpty() throws Exception {
m.clear();
assertTrue(m.isEmpty());
m.put(100L, 100L);
assertFalse(m.isEmpty());
m.remove(100L);
assertTrue(m.isEmpty());
}
@Test
public void testFindOnEmpty() throws Exception {
m.clear();
assertTrue(m.isEmpty());
assertNull(m.get(100L));
assertFalse(m.containsKey(100L));
assertEquals(0, m.tailMap(100L).entrySet().size());
}
@Test
public void testLowerKey() throws Exception {
assertEquals(csm.lowerKey(400L), m.lowerKey(400L));
assertEquals(csm.lowerKey(-1L), m.lowerKey(-1L));
for ( int i =0 ; i < 100; i ++) {
Long key = ThreadLocalRandom.current().nextLong();
assertEquals(csm.lowerKey(key), m.lowerKey(key));
}
}
@Test
public void testFloorEntry() throws Exception {
for ( int i =0 ; i < 100; i ++) {
Long key = ThreadLocalRandom.current().nextLong();
assertEquals(csm.floorEntry(key), m.floorEntry(key));
}
}
@Test
public void testFloorKey() throws Exception {
for ( int i =0 ; i < 100; i ++) {
Long key = ThreadLocalRandom.current().nextLong();
assertEquals(csm.floorKey(key), m.floorKey(key));
}
}
@Test
public void testCeilingKey() throws Exception {
assertEquals(csm.ceilingKey(4000L), m.ceilingKey(4000L));
assertEquals(csm.ceilingKey(400L), m.ceilingKey(400L));
assertEquals(csm.ceilingKey(-1L), m.ceilingKey(-1L));
for ( int i =0 ; i < 100; i ++) {
Long key = ThreadLocalRandom.current().nextLong();
assertEquals(csm.ceilingKey(key), m.ceilingKey(key));
}
}
@Test
public void testHigherKey() throws Exception {
assertEquals(csm.higherKey(4000L), m.higherKey(4000L));
assertEquals(csm.higherKey(400L), m.higherKey(400L));
assertEquals(csm.higherKey(-1L), m.higherKey(-1L));
for ( int i =0 ; i < 100; i ++) {
Long key = ThreadLocalRandom.current().nextLong();
assertEquals(csm.higherKey(key), m.higherKey(key));
}
}
@Test
public void testRemove() throws Exception {
for (Map.Entry<Long, Long> e:csm.entrySet()) {
assertEquals(csm.remove(e.getKey()), m.remove(e.getKey()));
assertEquals(null, m.remove(e.getKey()));
}
}
@Test
public void testReplace() throws Exception {
for (Map.Entry<Long, Long> e:csm.entrySet()) {
Long newValue = ThreadLocalRandom.current().nextLong();
assertEquals(csm.replace(e.getKey(), newValue), m.replace(e.getKey(), newValue));
}
assertEquals(null, m.replace(MAX_RAND + 100L, ThreadLocalRandom.current().nextLong()));
}
@Test
public void testReplace1() throws Exception {
for (Map.Entry<Long, Long> e: csm.entrySet()) {
Long newValue = ThreadLocalRandom.current().nextLong();
assertEquals(csm.replace(e.getKey(), e.getValue() + 1, newValue),
m.replace(e.getKey(), e.getValue() + 1, newValue));
assertEquals(csm.replace(e.getKey(), e.getValue(), newValue),
m.replace(e.getKey(), e.getValue(), newValue));
assertEquals(newValue, m.get(e.getKey()));
assertEquals(csm.get(e.getKey()), m.get(e.getKey()));
}
assertEquals(null, m.replace(MAX_RAND + 100L, ThreadLocalRandom.current().nextLong()));
}
@Test
public void testMultiAdd() throws InterruptedException {
Thread[] threads = new Thread[10];
for ( int i =0 ; i<threads.length; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
for ( int j = 0; j < 5000; j++) {
m.put(ThreadLocalRandom.current().nextLong(),
ThreadLocalRandom.current().nextLong());
}
}
});
}
for (Thread thread1 : threads) {
thread1.start();
}
for (Thread thread2 : threads) {
thread2.join();
}
}
@Test
public void testFirstKey() throws Exception {
assertEquals(csm.firstKey(), m.firstKey());
}
@Test
public void testLastKey() throws Exception {
assertEquals(csm.lastKey(), m.lastKey());
}
@Test
public void testFirstEntry() throws Exception {
assertEquals(csm.firstEntry().getKey(), m.firstEntry().getKey());
assertEquals(csm.firstEntry().getValue(), m.firstEntry().getValue());
assertEquals(csm.firstEntry(), m.firstEntry());
}
@Test
public void testLastEntry() throws Exception {
assertEquals(csm.lastEntry().getKey(), m.lastEntry().getKey());
assertEquals(csm.lastEntry().getValue(), m.lastEntry().getValue());
assertEquals(csm.lastEntry(), m.lastEntry());
}
@Test
public void testKeys() throws Exception {
for (Long key:csm.keySet()) {
//assertTrue(m.containsKey(key));
assertNotNull(m.get(key));
assertNotNull(m.remove(key));
assertNull(m.get(key));
}
}
@Test
public void testValues() throws Exception {
for (Long value:m.values()) {
assertTrue(csm.values().contains(value));
assertTrue(m.containsValue(value));
}
}
@Test
public void testTailMap() throws Exception {
Map<Long, Long> fromCsm = csm.tailMap(50L);
Map<Long, Long> fromM = m.tailMap(50L);
assertEquals(fromCsm, fromM);
for (Long value:m.keySet()) {
assertEquals(csm.tailMap(value), m.tailMap(value));
}
for ( long i = 0 ; i < 100; i++ ) {
long o = ThreadLocalRandom.current().nextLong(MAX_RAND);
assertEquals(csm.tailMap(o), m.tailMap(o));
}
}
@Test
public void testTailMapExclusive() throws Exception {
m.clear();
m.put(100L, 100L);
m.put(101L, 101L);
m.put(101L, 101L);
m.put(103L, 103L);
m.put(99L, 99L);
m.put(102L, 102L);
long n = 100L;
CopyOnWriteArrayMap<Long,Long> tm99 = (CopyOnWriteArrayMap<Long, Long>) m.tailMap(99L, false);
for (Map.Entry<Long,Long> e:tm99.entrySet()) {
assertEquals(new Long(n), e.getKey());
assertEquals(new Long(n), e.getValue());
n++;
}
}
@Test
public void testTailMapInclusive() throws Exception {
m.clear();
m.put(100L, 100L);
m.put(101L, 101L);
m.put(101L, 101L);
m.put(103L, 103L);
m.put(99L, 99L);
m.put(102L, 102L);
long n = 102;
CopyOnWriteArrayMap<Long,Long> tm102 = (CopyOnWriteArrayMap<Long, Long>) m.tailMap(102L, true);
for (Map.Entry<Long,Long> e:tm102.entrySet()) {
assertEquals(new Long(n), e.getKey());
assertEquals(new Long(n), e.getValue());
n++;
}
n = 99;
CopyOnWriteArrayMap<Long,Long> tm98 = (CopyOnWriteArrayMap<Long, Long>) m.tailMap(98L, true);
for (Map.Entry<Long,Long> e:tm98.entrySet()) {
assertEquals(new Long(n), e.getKey());
assertEquals(new Long(n), e.getValue());
n++;
}
}
@Test
public void testPut() throws Exception {
m.clear();
m.put(100L, 100L);
m.put(101L, 101L);
m.put(101L, 101L);
m.put(103L, 103L);
m.put(99L, 99L);
m.put(102L, 102L);
long n = 99;
for (Map.Entry<Long, Long> e:m.entrySet()) {
assertEquals(new Long(n), e.getKey());
assertEquals(new Long(n), e.getValue());
n++;
}
assertEquals(5, m.size());
assertEquals(false, m.isEmpty());
}
}