001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.types; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotNull; 023import static org.junit.Assert.assertNull; 024import static org.junit.Assert.assertTrue; 025 026import java.util.Map; 027import java.util.concurrent.ConcurrentNavigableMap; 028import java.util.concurrent.ConcurrentSkipListMap; 029import java.util.concurrent.ThreadLocalRandom; 030import org.apache.hadoop.hbase.HBaseClassTestRule; 031import org.apache.hadoop.hbase.testclassification.MiscTests; 032import org.apache.hadoop.hbase.testclassification.SmallTests; 033import org.junit.Before; 034import org.junit.ClassRule; 035import org.junit.Test; 036import org.junit.experimental.categories.Category; 037 038@Category({MiscTests.class, SmallTests.class}) 039public class TestCopyOnWriteMaps { 040 041 @ClassRule 042 public static final HBaseClassTestRule CLASS_RULE = 043 HBaseClassTestRule.forClass(TestCopyOnWriteMaps.class); 044 045 private static final int MAX_RAND = 10 * 1000 * 1000; 046 private ConcurrentNavigableMap<Long, Long> m; 047 private ConcurrentSkipListMap<Long, Long> csm; 048 049 @Before 050 public void setUp() { 051 m = new CopyOnWriteArrayMap<>(); 052 csm = new ConcurrentSkipListMap<>(); 053 054 for ( long i = 0 ; i < 10000; i++ ) { 055 long o = ThreadLocalRandom.current().nextLong(MAX_RAND); 056 m.put(i, o); 057 csm.put(i,o); 058 } 059 long o = ThreadLocalRandom.current().nextLong(MAX_RAND); 060 m.put(0L, o); 061 csm.put(0L,o); 062 } 063 064 @Test 065 public void testSize() throws Exception { 066 assertEquals("Size should always be equal", m.size(), csm.size()); 067 } 068 069 @Test 070 public void testIsEmpty() throws Exception { 071 m.clear(); 072 assertTrue(m.isEmpty()); 073 m.put(100L, 100L); 074 assertFalse(m.isEmpty()); 075 m.remove(100L); 076 assertTrue(m.isEmpty()); 077 } 078 079 @Test 080 public void testFindOnEmpty() throws Exception { 081 m.clear(); 082 assertTrue(m.isEmpty()); 083 assertNull(m.get(100L)); 084 assertFalse(m.containsKey(100L)); 085 assertEquals(0, m.tailMap(100L).entrySet().size()); 086 } 087 088 089 @Test 090 public void testLowerKey() throws Exception { 091 092 assertEquals(csm.lowerKey(400L), m.lowerKey(400L)); 093 assertEquals(csm.lowerKey(-1L), m.lowerKey(-1L)); 094 095 for ( int i =0 ; i < 100; i ++) { 096 Long key = ThreadLocalRandom.current().nextLong(); 097 assertEquals(csm.lowerKey(key), m.lowerKey(key)); 098 } 099 } 100 101 @Test 102 public void testFloorEntry() throws Exception { 103 for ( int i =0 ; i < 100; i ++) { 104 Long key = ThreadLocalRandom.current().nextLong(); 105 assertEquals(csm.floorEntry(key), m.floorEntry(key)); 106 } 107 } 108 109 @Test 110 public void testFloorKey() throws Exception { 111 for ( int i =0 ; i < 100; i ++) { 112 Long key = ThreadLocalRandom.current().nextLong(); 113 assertEquals(csm.floorKey(key), m.floorKey(key)); 114 } 115 } 116 117 @Test 118 public void testCeilingKey() throws Exception { 119 120 assertEquals(csm.ceilingKey(4000L), m.ceilingKey(4000L)); 121 assertEquals(csm.ceilingKey(400L), m.ceilingKey(400L)); 122 assertEquals(csm.ceilingKey(-1L), m.ceilingKey(-1L)); 123 124 for ( int i =0 ; i < 100; i ++) { 125 Long key = ThreadLocalRandom.current().nextLong(); 126 assertEquals(csm.ceilingKey(key), m.ceilingKey(key)); 127 } 128 } 129 130 @Test 131 public void testHigherKey() throws Exception { 132 133 assertEquals(csm.higherKey(4000L), m.higherKey(4000L)); 134 assertEquals(csm.higherKey(400L), m.higherKey(400L)); 135 assertEquals(csm.higherKey(-1L), m.higherKey(-1L)); 136 137 for ( int i =0 ; i < 100; i ++) { 138 Long key = ThreadLocalRandom.current().nextLong(); 139 assertEquals(csm.higherKey(key), m.higherKey(key)); 140 } 141 } 142 143 @Test 144 public void testRemove() throws Exception { 145 for (Map.Entry<Long, Long> e:csm.entrySet()) { 146 assertEquals(csm.remove(e.getKey()), m.remove(e.getKey())); 147 assertEquals(null, m.remove(e.getKey())); 148 } 149 } 150 151 @Test 152 public void testReplace() throws Exception { 153 for (Map.Entry<Long, Long> e:csm.entrySet()) { 154 Long newValue = ThreadLocalRandom.current().nextLong(); 155 assertEquals(csm.replace(e.getKey(), newValue), m.replace(e.getKey(), newValue)); 156 } 157 assertEquals(null, m.replace(MAX_RAND + 100L, ThreadLocalRandom.current().nextLong())); 158 } 159 160 @Test 161 public void testReplace1() throws Exception { 162 for (Map.Entry<Long, Long> e: csm.entrySet()) { 163 Long newValue = ThreadLocalRandom.current().nextLong(); 164 assertEquals(csm.replace(e.getKey(), e.getValue() + 1, newValue), 165 m.replace(e.getKey(), e.getValue() + 1, newValue)); 166 assertEquals(csm.replace(e.getKey(), e.getValue(), newValue), 167 m.replace(e.getKey(), e.getValue(), newValue)); 168 assertEquals(newValue, m.get(e.getKey())); 169 assertEquals(csm.get(e.getKey()), m.get(e.getKey())); 170 } 171 assertEquals(null, m.replace(MAX_RAND + 100L, ThreadLocalRandom.current().nextLong())); 172 } 173 174 @Test 175 public void testMultiAdd() throws InterruptedException { 176 177 Thread[] threads = new Thread[10]; 178 for ( int i =0 ; i<threads.length; i++) { 179 threads[i] = new Thread(new Runnable() { 180 @Override 181 public void run() { 182 for ( int j = 0; j < 5000; j++) { 183 m.put(ThreadLocalRandom.current().nextLong(), 184 ThreadLocalRandom.current().nextLong()); 185 } 186 } 187 }); 188 } 189 190 for (Thread thread1 : threads) { 191 thread1.start(); 192 } 193 194 for (Thread thread2 : threads) { 195 thread2.join(); 196 } 197 } 198 199 @Test 200 public void testFirstKey() throws Exception { 201 assertEquals(csm.firstKey(), m.firstKey()); 202 } 203 204 @Test 205 public void testLastKey() throws Exception { 206 assertEquals(csm.lastKey(), m.lastKey()); 207 } 208 209 @Test 210 public void testFirstEntry() throws Exception { 211 assertEquals(csm.firstEntry().getKey(), m.firstEntry().getKey()); 212 assertEquals(csm.firstEntry().getValue(), m.firstEntry().getValue()); 213 assertEquals(csm.firstEntry(), m.firstEntry()); 214 } 215 216 @Test 217 public void testLastEntry() throws Exception { 218 assertEquals(csm.lastEntry().getKey(), m.lastEntry().getKey()); 219 assertEquals(csm.lastEntry().getValue(), m.lastEntry().getValue()); 220 assertEquals(csm.lastEntry(), m.lastEntry()); 221 } 222 223 @Test 224 public void testKeys() throws Exception { 225 for (Long key:csm.keySet()) { 226 //assertTrue(m.containsKey(key)); 227 assertNotNull(m.get(key)); 228 assertNotNull(m.remove(key)); 229 assertNull(m.get(key)); 230 } 231 } 232 233 @Test 234 public void testValues() throws Exception { 235 for (Long value:m.values()) { 236 assertTrue(csm.values().contains(value)); 237 assertTrue(m.containsValue(value)); 238 } 239 } 240 241 @Test 242 public void testTailMap() throws Exception { 243 Map<Long, Long> fromCsm = csm.tailMap(50L); 244 Map<Long, Long> fromM = m.tailMap(50L); 245 assertEquals(fromCsm, fromM); 246 for (Long value:m.keySet()) { 247 assertEquals(csm.tailMap(value), m.tailMap(value)); 248 } 249 250 for ( long i = 0 ; i < 100; i++ ) { 251 long o = ThreadLocalRandom.current().nextLong(MAX_RAND); 252 assertEquals(csm.tailMap(o), m.tailMap(o)); 253 } 254 } 255 256 @Test 257 public void testTailMapExclusive() throws Exception { 258 m.clear(); 259 m.put(100L, 100L); 260 m.put(101L, 101L); 261 m.put(101L, 101L); 262 m.put(103L, 103L); 263 m.put(99L, 99L); 264 m.put(102L, 102L); 265 266 long n = 100L; 267 CopyOnWriteArrayMap<Long,Long> tm99 = (CopyOnWriteArrayMap<Long, Long>) m.tailMap(99L, false); 268 for (Map.Entry<Long,Long> e:tm99.entrySet()) { 269 assertEquals(Long.valueOf(n), e.getKey()); 270 assertEquals(Long.valueOf(n), e.getValue()); 271 n++; 272 } 273 } 274 275 @Test 276 public void testTailMapInclusive() throws Exception { 277 m.clear(); 278 m.put(100L, 100L); 279 m.put(101L, 101L); 280 m.put(101L, 101L); 281 m.put(103L, 103L); 282 m.put(99L, 99L); 283 m.put(102L, 102L); 284 285 long n = 102; 286 CopyOnWriteArrayMap<Long,Long> tm102 = (CopyOnWriteArrayMap<Long, Long>) m.tailMap(102L, true); 287 for (Map.Entry<Long,Long> e:tm102.entrySet()) { 288 assertEquals(Long.valueOf(n), e.getKey()); 289 assertEquals(Long.valueOf(n), e.getValue()); 290 n++; 291 } 292 n = 99; 293 CopyOnWriteArrayMap<Long,Long> tm98 = (CopyOnWriteArrayMap<Long, Long>) m.tailMap(98L, true); 294 for (Map.Entry<Long,Long> e:tm98.entrySet()) { 295 assertEquals(Long.valueOf(n), e.getKey()); 296 assertEquals(Long.valueOf(n), e.getValue()); 297 n++; 298 } 299 } 300 301 @Test 302 public void testPut() throws Exception { 303 m.clear(); 304 m.put(100L, 100L); 305 m.put(101L, 101L); 306 m.put(101L, 101L); 307 m.put(103L, 103L); 308 m.put(99L, 99L); 309 m.put(102L, 102L); 310 long n = 99; 311 312 for (Map.Entry<Long, Long> e:m.entrySet()) { 313 assertEquals(Long.valueOf(n), e.getKey()); 314 assertEquals(Long.valueOf(n), e.getValue()); 315 n++; 316 } 317 assertEquals(5, m.size()); 318 assertFalse(m.isEmpty()); 319 } 320}