1 // This file is part of OpenTSDB.
2 // Copyright (C) 2010-2012  The OpenTSDB Authors.
3 //
4 // This program is free software: you can redistribute it and/or modify it
5 // under the terms of the GNU Lesser General Public License as published by
6 // the Free Software Foundation, either version 2.1 of the License, or (at your
7 // option) any later version.  This program is distributed in the hope that it
8 // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
9 // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
10 // General Public License for more details.  You should have received a copy
11 // of the GNU Lesser General Public License along with this program.  If not,
12 // see <http://www.gnu.org/licenses/>.
13 package net.opentsdb.uid;
14 
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.List;
18 import java.util.Map;
19 
20 import com.stumbleupon.async.Callback;
21 import com.stumbleupon.async.Deferred;
22 
23 import net.opentsdb.core.Const;
24 import net.opentsdb.core.TSDB;
25 import net.opentsdb.core.BaseTsdbTest.UnitTestException;
26 import net.opentsdb.storage.MockBase;
27 import net.opentsdb.uid.UniqueId.UniqueIdType;
28 import net.opentsdb.utils.Config;
29 
30 import org.hbase.async.AtomicIncrementRequest;
31 import org.hbase.async.Bytes;
32 import org.hbase.async.DeleteRequest;
33 import org.hbase.async.GetRequest;
34 import org.hbase.async.HBaseClient;
35 import org.hbase.async.HBaseException;
36 import org.hbase.async.KeyValue;
37 import org.hbase.async.PutRequest;
38 import org.hbase.async.Scanner;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 
42 import org.mockito.ArgumentMatcher;
43 import org.mockito.InOrder;
44 import org.mockito.invocation.InvocationOnMock;
45 import org.mockito.stubbing.Answer;
46 
47 import static org.junit.Assert.*;
48 import static org.mockito.Matchers.any;
49 import static org.mockito.Matchers.anyMapOf;
50 import static org.mockito.Matchers.anyString;
51 import static org.mockito.Mockito.anyInt;
52 import static org.mockito.Mockito.argThat;
53 import static org.mockito.Mockito.eq;
54 import static org.mockito.Mockito.inOrder;
55 import static org.mockito.Mockito.never;
56 import static org.mockito.Mockito.times;
57 import static org.mockito.Mockito.verify;
58 import static org.mockito.Mockito.when;
59 
60 import org.powermock.api.mockito.PowerMockito;
61 import org.powermock.core.classloader.annotations.PowerMockIgnore;
62 import org.powermock.core.classloader.annotations.PrepareForTest;
63 import org.powermock.modules.junit4.PowerMockRunner;
64 
65 import static org.powermock.api.mockito.PowerMockito.mock;
66 
67 @RunWith(PowerMockRunner.class)
68 // "Classloader hell"...  It's real.  Tell PowerMock to ignore these classes
69 // because they fiddle with the class loader.  We don't test them anyway.
70 @PowerMockIgnore({"javax.management.*", "javax.xml.*",
71                   "ch.qos.*", "org.slf4j.*",
72                   "com.sum.*", "org.xml.*"})
73 @PrepareForTest({ HBaseClient.class, TSDB.class, Config.class, Scanner.class,
74   RandomUniqueId.class, Const.class, Deferred.class })
75 public final class TestUniqueId {
76   private static final byte[] table = { 't', 's', 'd', 'b', '-', 'u', 'i', 'd' };
77   private static final byte[] ID = { 'i', 'd' };
78   private static final byte[] NAME = { 'n', 'a', 'm', 'e' };
79   private static final String METRIC = "metric";
80   private static final byte[] METRIC_ARRAY = { 'm', 'e', 't', 'r', 'i', 'c' };
81   private static final String TAGK = "tagk";
82   private static final byte[] TAGK_ARRAY = { 't', 'a', 'g', 'k' };
83   private static final String TAGV = "tagv";
84   private static final byte[] TAGV_ARRAY = { 't', 'a', 'g', 'v' };
85   private static final byte[] UID = new byte[] { 0, 0, 1 };
86   private TSDB tsdb = mock(TSDB.class);
87   private HBaseClient client = mock(HBaseClient.class);
88   private UniqueId uid;
89   private MockBase storage;
90 
91   @Test(expected=IllegalArgumentException.class)
testCtorZeroWidth()92   public void testCtorZeroWidth() {
93     uid = new UniqueId(client, table, METRIC, 0);
94   }
95 
96   @Test(expected=IllegalArgumentException.class)
testCtorNegativeWidth()97   public void testCtorNegativeWidth() {
98     uid = new UniqueId(client, table, METRIC, -1);
99   }
100 
101   @Test(expected=IllegalArgumentException.class)
testCtorEmptyKind()102   public void testCtorEmptyKind() {
103     uid = new UniqueId(client, table, "", 3);
104   }
105 
106   @Test(expected=IllegalArgumentException.class)
testCtorLargeWidth()107   public void testCtorLargeWidth() {
108     uid = new UniqueId(client, table, METRIC, 9);
109   }
110 
111   @Test
kindEqual()112   public void kindEqual() {
113     uid = new UniqueId(client, table, METRIC, 3);
114     assertEquals(METRIC, uid.kind());
115   }
116 
117   @Test
widthEqual()118   public void widthEqual() {
119     uid = new UniqueId(client, table, METRIC, 3);
120     assertEquals(3, uid.width());
121   }
122 
123   @Test
getNameSuccessfulHBaseLookup()124   public void getNameSuccessfulHBaseLookup() {
125     uid = new UniqueId(client, table, METRIC, 3);
126     final byte[] id = { 0, 'a', 0x42 };
127     final byte[] byte_name = { 'f', 'o', 'o' };
128 
129     ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
130     kvs.add(new KeyValue(id, ID, METRIC_ARRAY, byte_name));
131     when(client.get(anyGet()))
132       .thenReturn(Deferred.fromResult(kvs));
133 
134     assertEquals("foo", uid.getName(id));
135     // Should be a cache hit ...
136     assertEquals("foo", uid.getName(id));
137 
138     assertEquals(1, uid.cacheHits());
139     assertEquals(1, uid.cacheMisses());
140     assertEquals(2, uid.cacheSize());
141 
142     // ... so verify there was only one HBase Get.
143     verify(client).get(anyGet());
144   }
145 
146   @Test
getNameWithErrorDuringHBaseLookup()147   public void getNameWithErrorDuringHBaseLookup() {
148     uid = new UniqueId(client, table, METRIC, 3);
149     final byte[] id = { 0, 'a', 0x42 };
150     final byte[] byte_name = { 'f', 'o', 'o' };
151 
152     HBaseException hbe = mock(HBaseException.class);
153 
154     ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
155     kvs.add(new KeyValue(id, ID, METRIC_ARRAY, byte_name));
156     when(client.get(anyGet()))
157       .thenThrow(hbe)
158       .thenReturn(Deferred.fromResult(kvs));
159 
160     // 1st calls fails.
161     try {
162       uid.getName(id);
163       fail("HBaseException should have been thrown.");
164     } catch (HBaseException e) {
165       assertSame(hbe, e);  // OK.
166     }
167 
168     // 2nd call succeeds.
169     assertEquals("foo", uid.getName(id));
170 
171     assertEquals(0, uid.cacheHits());
172     assertEquals(2, uid.cacheMisses());  // 1st (failed) attempt + 2nd.
173     assertEquals(2, uid.cacheSize());
174 
175     verify(client, times(2)).get(anyGet());
176   }
177 
178   @Test(expected=NoSuchUniqueId.class)
getNameForNonexistentId()179   public void getNameForNonexistentId() {
180     uid = new UniqueId(client, table, METRIC, 3);
181 
182     when(client.get(anyGet()))
183       .thenReturn(Deferred.fromResult(new ArrayList<KeyValue>(0)));
184 
185     uid.getName(new byte[] { 1, 2, 3 });
186   }
187 
188   @Test(expected=IllegalArgumentException.class)
getNameWithInvalidId()189   public void getNameWithInvalidId() {
190     uid = new UniqueId(client, table, METRIC, 3);
191 
192     uid.getName(new byte[] { 1 });
193   }
194 
195   @Test
getIdSuccessfulHBaseLookup()196   public void getIdSuccessfulHBaseLookup() {
197     uid = new UniqueId(client, table, METRIC, 3);
198     final byte[] id = { 0, 'a', 0x42 };
199     final byte[] byte_name = { 'f', 'o', 'o' };
200 
201     ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
202     kvs.add(new KeyValue(byte_name, ID, METRIC_ARRAY, id));
203     when(client.get(anyGet()))
204       .thenReturn(Deferred.fromResult(kvs));
205 
206     assertArrayEquals(id, uid.getId("foo"));
207     // Should be a cache hit ...
208     assertArrayEquals(id, uid.getId("foo"));
209     // Should be a cache hit too ...
210     assertArrayEquals(id, uid.getId("foo"));
211 
212     assertEquals(2, uid.cacheHits());
213     assertEquals(1, uid.cacheMisses());
214     assertEquals(2, uid.cacheSize());
215 
216     // ... so verify there was only one HBase Get.
217     verify(client).get(anyGet());
218   }
219 
220   // The table contains IDs encoded on 2 bytes but the instance wants 3.
221   @Test(expected=IllegalStateException.class)
getIdMisconfiguredWidth()222   public void getIdMisconfiguredWidth() {
223     uid = new UniqueId(client, table, METRIC, 3);
224     final byte[] id = { 'a', 0x42 };
225     final byte[] byte_name = { 'f', 'o', 'o' };
226 
227     ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
228     kvs.add(new KeyValue(byte_name, ID, METRIC_ARRAY, id));
229     when(client.get(anyGet()))
230       .thenReturn(Deferred.fromResult(kvs));
231 
232     uid.getId("foo");
233   }
234 
235   @Test(expected=NoSuchUniqueName.class)
getIdForNonexistentName()236   public void getIdForNonexistentName() {
237     uid = new UniqueId(client, table, METRIC, 3);
238 
239     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
240       .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
241     // Watch this! ______,^   I'm writing C++ in Java!
242 
243     uid.getId("foo");
244   }
245 
246   @Test
getOrCreateIdWithExistingId()247   public void getOrCreateIdWithExistingId() {
248     uid = new UniqueId(client, table, METRIC, 3);
249     final byte[] id = { 0, 'a', 0x42 };
250     final byte[] byte_name = { 'f', 'o', 'o' };
251 
252     ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
253     kvs.add(new KeyValue(byte_name, ID, METRIC_ARRAY, id));
254     when(client.get(anyGet()))
255       .thenReturn(Deferred.fromResult(kvs));
256 
257     assertArrayEquals(id, uid.getOrCreateId("foo"));
258     // Should be a cache hit ...
259     assertArrayEquals(id, uid.getOrCreateId("foo"));
260     assertEquals(1, uid.cacheHits());
261     assertEquals(1, uid.cacheMisses());
262     assertEquals(2, uid.cacheSize());
263 
264     // ... so verify there was only one HBase Get.
265     verify(client).get(anyGet());
266   }
267 
268   @Test  // Test the creation of an ID with no problem.
getOrCreateIdAssignFilterOK()269   public void getOrCreateIdAssignFilterOK() {
270     uid = new UniqueId(client, table, METRIC, 3);
271     final byte[] id = { 0, 0, 5 };
272     final Config config = mock(Config.class);
273     when(config.enable_realtime_uid()).thenReturn(false);
274     final TSDB tsdb = mock(TSDB.class);
275     when(tsdb.getConfig()).thenReturn(config);
276     uid.setTSDB(tsdb);
277     final UniqueIdFilterPlugin filter = mock(UniqueIdFilterPlugin.class);
278     when(filter.fillterUIDAssignments()).thenReturn(true);
279     when(filter.allowUIDAssignment(any(UniqueIdType.class), anyString(),
280         anyString(), anyMapOf(String.class, String.class)))
281       .thenReturn(Deferred.fromResult(true));
282     when(tsdb.getUidFilter()).thenReturn(filter);
283 
284     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
285       .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
286     // Watch this! ______,^   I'm writing C++ in Java!
287 
288     when(client.atomicIncrement(incrementForRow(MAXID)))
289       .thenReturn(Deferred.fromResult(5L));
290 
291     when(client.compareAndSet(anyPut(), emptyArray()))
292       .thenReturn(Deferred.fromResult(true))
293       .thenReturn(Deferred.fromResult(true));
294 
295     assertArrayEquals(id, uid.getOrCreateId("foo"));
296     // Should be a cache hit since we created that entry.
297     assertArrayEquals(id, uid.getOrCreateId("foo"));
298     // Should be a cache hit too for the same reason.
299     assertEquals("foo", uid.getName(id));
300 
301     verify(client).get(anyGet()); // Initial Get.
302     verify(client).atomicIncrement(incrementForRow(MAXID));
303     // Reverse + forward mappings.
304     verify(client, times(2)).compareAndSet(anyPut(), emptyArray());
305     verify(filter, times(1)).allowUIDAssignment(any(UniqueIdType.class), anyString(),
306         anyString(), anyMapOf(String.class, String.class));
307   }
308 
309   @Test (expected = FailedToAssignUniqueIdException.class)
getOrCreateIdAssignFilterBlocked()310   public void getOrCreateIdAssignFilterBlocked() {
311     uid = new UniqueId(client, table, METRIC, 3);
312     final Config config = mock(Config.class);
313     when(config.enable_realtime_uid()).thenReturn(false);
314     final TSDB tsdb = mock(TSDB.class);
315     when(tsdb.getConfig()).thenReturn(config);
316     uid.setTSDB(tsdb);
317     final UniqueIdFilterPlugin filter = mock(UniqueIdFilterPlugin.class);
318     when(filter.fillterUIDAssignments()).thenReturn(true);
319     when(filter.allowUIDAssignment(any(UniqueIdType.class), anyString(),
320         anyString(), anyMapOf(String.class, String.class)))
321       .thenReturn(Deferred.fromResult(false));
322     when(tsdb.getUidFilter()).thenReturn(filter);
323 
324     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
325             .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
326     // Watch this! ______,^   I'm writing C++ in Java!
327 
328     when(client.atomicIncrement(incrementForRow(MAXID)))
329             .thenReturn(Deferred.fromResult(5L));
330 
331     when(client.compareAndSet(anyPut(), emptyArray()))
332             .thenReturn(Deferred.fromResult(true))
333             .thenReturn(Deferred.fromResult(true));
334 
335     uid.getOrCreateId("foo");
336   }
337 
338   @Test(expected = RuntimeException.class)
getOrCreateIdAssignFilterReturnException()339   public void getOrCreateIdAssignFilterReturnException() {
340     uid = new UniqueId(client, table, METRIC, 3);
341     final Config config = mock(Config.class);
342     when(config.enable_realtime_uid()).thenReturn(false);
343     final TSDB tsdb = mock(TSDB.class);
344     when(tsdb.getConfig()).thenReturn(config);
345     uid.setTSDB(tsdb);
346     final UniqueIdFilterPlugin filter = mock(UniqueIdFilterPlugin.class);
347     when(filter.fillterUIDAssignments()).thenReturn(true);
348     when(filter.allowUIDAssignment(any(UniqueIdType.class), anyString(),
349         anyString(), anyMapOf(String.class, String.class)))
350       .thenReturn(Deferred.<Boolean>fromError(new UnitTestException()));
351     when(tsdb.getUidFilter()).thenReturn(filter);
352 
353     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
354             .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
355     // Watch this! ______,^   I'm writing C++ in Java!
356 
357     when(client.atomicIncrement(incrementForRow(MAXID)))
358             .thenReturn(Deferred.fromResult(5L));
359 
360     when(client.compareAndSet(anyPut(), emptyArray()))
361             .thenReturn(Deferred.fromResult(true))
362             .thenReturn(Deferred.fromResult(true));
363 
364     uid.getOrCreateId("foo");
365   }
366 
367   @Test(expected = RuntimeException.class)
getOrCreateIdAssignFilterThrowsException()368   public void getOrCreateIdAssignFilterThrowsException() {
369     uid = new UniqueId(client, table, METRIC, 3);
370     final Config config = mock(Config.class);
371     when(config.enable_realtime_uid()).thenReturn(false);
372     final TSDB tsdb = mock(TSDB.class);
373     when(tsdb.getConfig()).thenReturn(config);
374     uid.setTSDB(tsdb);
375     final UniqueIdFilterPlugin filter = mock(UniqueIdFilterPlugin.class);
376     when(filter.fillterUIDAssignments()).thenReturn(true);
377     when(filter.allowUIDAssignment(any(UniqueIdType.class), anyString(),
378         anyString(), anyMapOf(String.class, String.class)))
379       .thenThrow(new UnitTestException());
380     when(tsdb.getUidFilter()).thenReturn(filter);
381 
382     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
383             .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
384     // Watch this! ______,^   I'm writing C++ in Java!
385 
386     when(client.atomicIncrement(incrementForRow(MAXID)))
387             .thenReturn(Deferred.fromResult(5L));
388 
389     when(client.compareAndSet(anyPut(), emptyArray()))
390             .thenReturn(Deferred.fromResult(true))
391             .thenReturn(Deferred.fromResult(true));
392 
393     uid.getOrCreateId("foo");
394   }
395 
396   @Test
getOrCreateIdAsyncAssignFilterOK()397   public void getOrCreateIdAsyncAssignFilterOK() throws Exception {
398     uid = new UniqueId(client, table, METRIC, 3);
399     final byte[] id = { 0, 0, 5 };
400     final Config config = mock(Config.class);
401     when(config.enable_realtime_uid()).thenReturn(false);
402     final TSDB tsdb = mock(TSDB.class);
403     when(tsdb.getConfig()).thenReturn(config);
404     uid.setTSDB(tsdb);
405     final UniqueIdFilterPlugin filter = mock(UniqueIdFilterPlugin.class);
406     when(filter.fillterUIDAssignments()).thenReturn(true);
407     when(filter.allowUIDAssignment(any(UniqueIdType.class), anyString(),
408         anyString(), anyMapOf(String.class, String.class)))
409       .thenReturn(Deferred.fromResult(true));
410     when(tsdb.getUidFilter()).thenReturn(filter);
411     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
412       .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
413     // Watch this! ______,^   I'm writing C++ in Java!
414 
415     when(client.atomicIncrement(incrementForRow(MAXID)))
416       .thenReturn(Deferred.fromResult(5L));
417 
418     when(client.compareAndSet(anyPut(), emptyArray()))
419       .thenReturn(Deferred.fromResult(true))
420       .thenReturn(Deferred.fromResult(true));
421 
422     assertArrayEquals(id, uid.getOrCreateIdAsync("foo").join());
423     // Should be a cache hit since we created that entry.
424     assertArrayEquals(id, uid.getOrCreateIdAsync("foo").join());
425     // Should be a cache hit too for the same reason.
426     assertEquals("foo", uid.getName(id));
427 
428     verify(client).get(anyGet()); // Initial Get.
429     verify(client).atomicIncrement(incrementForRow(MAXID));
430     // Reverse + forward mappings.
431     verify(client, times(2)).compareAndSet(anyPut(), emptyArray());
432     verify(filter, times(1)).allowUIDAssignment(any(UniqueIdType.class), anyString(),
433         anyString(), anyMapOf(String.class, String.class));
434   }
435 
436   @Test (expected = FailedToAssignUniqueIdException.class)
getOrCreateIdAsyncAssignFilterBlocked()437   public void getOrCreateIdAsyncAssignFilterBlocked() throws Exception {
438     uid = new UniqueId(client, table, METRIC, 3);
439     final Config config = mock(Config.class);
440     when(config.enable_realtime_uid()).thenReturn(false);
441     final TSDB tsdb = mock(TSDB.class);
442     when(tsdb.getConfig()).thenReturn(config);
443     uid.setTSDB(tsdb);
444     final UniqueIdFilterPlugin filter = mock(UniqueIdFilterPlugin.class);
445     when(filter.fillterUIDAssignments()).thenReturn(true);
446     when(filter.allowUIDAssignment(any(UniqueIdType.class), anyString(),
447         anyString(), anyMapOf(String.class, String.class)))
448       .thenReturn(Deferred.fromResult(false));
449     when(tsdb.getUidFilter()).thenReturn(filter);
450     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
451       .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
452 
453     when(client.atomicIncrement(incrementForRow(MAXID)))
454       .thenReturn(Deferred.fromResult(5L));
455 
456     when(client.compareAndSet(anyPut(), emptyArray()))
457       .thenReturn(Deferred.fromResult(true))
458       .thenReturn(Deferred.fromResult(true));
459 
460     uid.getOrCreateIdAsync("foo").join();
461   }
462 
463   @Test (expected = UnitTestException.class)
getOrCreateIdAsyncAssignFilterReturnException()464   public void getOrCreateIdAsyncAssignFilterReturnException() throws Exception {
465     uid = new UniqueId(client, table, METRIC, 3);
466     final Config config = mock(Config.class);
467     when(config.enable_realtime_uid()).thenReturn(false);
468     final TSDB tsdb = mock(TSDB.class);
469     when(tsdb.getConfig()).thenReturn(config);
470     uid.setTSDB(tsdb);
471     final UniqueIdFilterPlugin filter = mock(UniqueIdFilterPlugin.class);
472     when(filter.fillterUIDAssignments()).thenReturn(true);
473     when(filter.allowUIDAssignment(any(UniqueIdType.class), anyString(),
474         anyString(), anyMapOf(String.class, String.class)))
475       .thenReturn(Deferred.<Boolean>fromError(new UnitTestException()));
476     when(tsdb.getUidFilter()).thenReturn(filter);
477     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
478       .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
479 
480     when(client.atomicIncrement(incrementForRow(MAXID)))
481       .thenReturn(Deferred.fromResult(5L));
482 
483     when(client.compareAndSet(anyPut(), emptyArray()))
484       .thenReturn(Deferred.fromResult(true))
485       .thenReturn(Deferred.fromResult(true));
486 
487     uid.getOrCreateIdAsync("foo").join();
488   }
489 
490   @Test (expected = UnitTestException.class)
getOrCreateIdAsyncAssignFilterThrowsException()491   public void getOrCreateIdAsyncAssignFilterThrowsException() throws Exception {
492     uid = new UniqueId(client, table, METRIC, 3);
493     final Config config = mock(Config.class);
494     when(config.enable_realtime_uid()).thenReturn(false);
495     final TSDB tsdb = mock(TSDB.class);
496     when(tsdb.getConfig()).thenReturn(config);
497     uid.setTSDB(tsdb);
498     final UniqueIdFilterPlugin filter = mock(UniqueIdFilterPlugin.class);
499     when(filter.fillterUIDAssignments()).thenReturn(true);
500     when(filter.allowUIDAssignment(any(UniqueIdType.class), anyString(),
501         anyString(),anyMapOf(String.class, String.class)))
502       .thenThrow(new UnitTestException());
503     when(tsdb.getUidFilter()).thenReturn(filter);
504     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
505       .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
506 
507     when(client.atomicIncrement(incrementForRow(MAXID)))
508       .thenReturn(Deferred.fromResult(5L));
509 
510     when(client.compareAndSet(anyPut(), emptyArray()))
511       .thenReturn(Deferred.fromResult(true))
512       .thenReturn(Deferred.fromResult(true));
513 
514     uid.getOrCreateIdAsync("foo").join();
515   }
516 
517   @Test  // Test the creation of an ID when unable to increment MAXID
getOrCreateIdUnableToIncrementMaxId()518   public void getOrCreateIdUnableToIncrementMaxId() throws Exception {
519     PowerMockito.mockStatic(Thread.class);
520 
521     uid = new UniqueId(client, table, METRIC, 3);
522 
523     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
524       .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
525     // Watch this! ______,^   I'm writing C++ in Java!
526 
527     HBaseException hbe = fakeHBaseException();
528     when(client.atomicIncrement(incrementForRow(MAXID)))
529       .thenThrow(hbe);
530     PowerMockito.doNothing().when(Thread.class); Thread.sleep(anyInt());
531 
532     try {
533       uid.getOrCreateId("foo");
534       fail("HBaseException should have been thrown!");
535     } catch (HBaseException e) {
536       assertSame(hbe, e);
537     }
538   }
539 
540   @Test  // Test the creation of an ID with a race condition.
getOrCreateIdAssignIdWithRaceCondition()541   public void getOrCreateIdAssignIdWithRaceCondition() {
542     // Simulate a race between client A and client B.
543     // A does a Get and sees that there's no ID for this name.
544     // B does a Get and sees that there's no ID too, and B actually goes
545     // through the entire process to create the ID.
546     // Then A attempts to go through the process and should discover that the
547     // ID has already been assigned.
548 
549     uid = new UniqueId(client, table, METRIC, 3); // Used by client A.
550     HBaseClient client_b = mock(HBaseClient.class); // For client B.
551     final UniqueId uid_b = new UniqueId(client_b, table, METRIC, 3);
552 
553     final byte[] id = { 0, 0, 5 };
554     final byte[] byte_name = { 'f', 'o', 'o' };
555     final ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
556     kvs.add(new KeyValue(byte_name, ID, METRIC_ARRAY, id));
557 
558     final Deferred<ArrayList<KeyValue>> d =
559       PowerMockito.spy(new Deferred<ArrayList<KeyValue>>());
560     when(client.get(anyGet()))
561       .thenReturn(d)
562       .thenReturn(Deferred.fromResult(kvs));
563 
564     final Answer<byte[]> the_race = new Answer<byte[]>() {
565       public byte[] answer(final InvocationOnMock unused_invocation) throws Exception {
566         // While answering A's first Get, B doest a full getOrCreateId.
567         assertArrayEquals(id, uid_b.getOrCreateId("foo"));
568         d.callback(null);
569         return (byte[]) ((Deferred) d).join();
570       }
571     };
572 
573     // Start the race when answering A's first Get.
574     try {
575       PowerMockito.doAnswer(the_race).when(d).joinUninterruptibly();
576     } catch (Exception e) {
577       fail("Should never happen: " + e);
578     }
579 
580     when(client_b.get(anyGet())) // null => ID doesn't exist.
581       .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
582     // Watch this! ______,^ I'm writing C++ in Java!
583 
584     when(client_b.atomicIncrement(incrementForRow(MAXID)))
585       .thenReturn(Deferred.fromResult(5L));
586 
587     when(client_b.compareAndSet(anyPut(), emptyArray()))
588       .thenReturn(Deferred.fromResult(true))
589       .thenReturn(Deferred.fromResult(true));
590 
591     // Now that B is finished, A proceeds and allocates a UID that will be
592     // wasted, and creates the reverse mapping, but fails at creating the
593     // forward mapping.
594     when(client.atomicIncrement(incrementForRow(MAXID)))
595       .thenReturn(Deferred.fromResult(6L));
596 
597     when(client.compareAndSet(anyPut(), emptyArray()))
598       .thenReturn(Deferred.fromResult(true)) // Orphan reverse mapping.
599       .thenReturn(Deferred.fromResult(false)); // Already CAS'ed by A.
600 
601     // Start the execution.
602     assertArrayEquals(id, uid.getOrCreateId("foo"));
603 
604     // Verify the order of execution too.
605     final InOrder order = inOrder(client, client_b);
606     order.verify(client).get(anyGet()); // 1st Get for A.
607     order.verify(client_b).get(anyGet()); // 1st Get for B.
608     order.verify(client_b).atomicIncrement(incrementForRow(MAXID));
609     order.verify(client_b, times(2)).compareAndSet(anyPut(), // both mappings.
610                                                    emptyArray());
611     order.verify(client).atomicIncrement(incrementForRow(MAXID));
612     order.verify(client, times(2)).compareAndSet(anyPut(), // both mappings.
613                                                  emptyArray());
614     order.verify(client).get(anyGet()); // A retries and gets it.
615   }
616 
617   @Test
618   // Test the creation of an ID when all possible IDs are already in use
getOrCreateIdWithOverflow()619   public void getOrCreateIdWithOverflow() {
620     uid = new UniqueId(client, table, METRIC, 1);  // IDs are only on 1 byte.
621 
622     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
623       .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
624     // Watch this! ______,^   I'm writing C++ in Java!
625 
626     // Update once HBASE-2292 is fixed:
627     when(client.atomicIncrement(incrementForRow(MAXID)))
628       .thenReturn(Deferred.fromResult(256L));
629 
630     try {
631       final byte[] id = uid.getOrCreateId("foo");
632       fail("IllegalArgumentException should have been thrown but instead "
633            + " this was returned id=" + Arrays.toString(id));
634     } catch (IllegalStateException e) {
635       // OK.
636     }
637 
638     verify(client, times(1)).get(anyGet());  // Initial Get.
639     verify(client).atomicIncrement(incrementForRow(MAXID));
640   }
641 
642   @Test  // ICV throws an exception, we can't get an ID.
getOrCreateIdWithICVFailure()643   public void getOrCreateIdWithICVFailure() {
644     uid = new UniqueId(client, table, METRIC, 3);
645     final Config config = mock(Config.class);
646     when(config.enable_realtime_uid()).thenReturn(false);
647     final TSDB tsdb = mock(TSDB.class);
648     when(tsdb.getConfig()).thenReturn(config);
649     uid.setTSDB(tsdb);
650 
651     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
652       .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
653     // Watch this! ______,^   I'm writing C++ in Java!
654 
655     // Update once HBASE-2292 is fixed:
656     HBaseException hbe = fakeHBaseException();
657     when(client.atomicIncrement(incrementForRow(MAXID)))
658       .thenReturn(Deferred.<Long>fromError(hbe))
659       .thenReturn(Deferred.fromResult(5L));
660 
661     when(client.compareAndSet(anyPut(), emptyArray()))
662       .thenReturn(Deferred.fromResult(true))
663       .thenReturn(Deferred.fromResult(true));
664 
665     final byte[] id = { 0, 0, 5 };
666     assertArrayEquals(id, uid.getOrCreateId("foo"));
667     verify(client, times(1)).get(anyGet()); // Initial Get.
668     // First increment (failed) + retry.
669     verify(client, times(2)).atomicIncrement(incrementForRow(MAXID));
670     // Reverse + forward mappings.
671     verify(client, times(2)).compareAndSet(anyPut(), emptyArray());
672   }
673 
674   @Test  // Test that the reverse mapping is created before the forward one.
getOrCreateIdPutsReverseMappingFirst()675   public void getOrCreateIdPutsReverseMappingFirst() {
676     uid = new UniqueId(client, table, METRIC, 3);
677     final Config config = mock(Config.class);
678     when(config.enable_realtime_uid()).thenReturn(false);
679     final TSDB tsdb = mock(TSDB.class);
680     when(tsdb.getConfig()).thenReturn(config);
681     uid.setTSDB(tsdb);
682 
683     when(client.get(anyGet()))      // null  =>  ID doesn't exist.
684       .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
685     // Watch this! ______,^   I'm writing C++ in Java!
686 
687     when(client.atomicIncrement(incrementForRow(MAXID)))
688       .thenReturn(Deferred.fromResult(6L));
689 
690     when(client.compareAndSet(anyPut(), emptyArray()))
691       .thenReturn(Deferred.fromResult(true))
692       .thenReturn(Deferred.fromResult(true));
693 
694     final byte[] id = { 0, 0, 6 };
695     final byte[] row = { 'f', 'o', 'o' };
696     assertArrayEquals(id, uid.getOrCreateId("foo"));
697 
698     final InOrder order = inOrder(client);
699     order.verify(client).get(anyGet());            // Initial Get.
700     order.verify(client).atomicIncrement(incrementForRow(MAXID));
701     order.verify(client).compareAndSet(putForRow(id), emptyArray());
702     order.verify(client).compareAndSet(putForRow(row), emptyArray());
703   }
704 
705   @Test
getOrCreateIdRandom()706   public void getOrCreateIdRandom() {
707     PowerMockito.mockStatic(RandomUniqueId.class);
708     uid = new UniqueId(client, table, METRIC, 3, true);
709     final long id = 42L;
710     final byte[] id_array = { 0, 0, 0x2A };
711 
712     when(RandomUniqueId.getRandomUID()).thenReturn(id);
713     when(client.get(any(GetRequest.class)))
714       .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
715 
716     when(client.compareAndSet(any(PutRequest.class), any(byte[].class)))
717       .thenReturn(Deferred.fromResult(true))
718       .thenReturn(Deferred.fromResult(true));
719 
720     assertArrayEquals(id_array, uid.getOrCreateId("foo"));
721     // Should be a cache hit ...
722     assertArrayEquals(id_array, uid.getOrCreateId("foo"));
723     assertEquals(1, uid.cacheHits());
724     assertEquals(1, uid.cacheMisses());
725     assertEquals(2, uid.cacheSize());
726     assertEquals(0, uid.randomIdCollisions());
727     // ... so verify there was only one HBase Get.
728     verify(client).get(any(GetRequest.class));
729   }
730 
731   @Test
getOrCreateIdRandomCollision()732   public void getOrCreateIdRandomCollision() {
733     PowerMockito.mockStatic(RandomUniqueId.class);
734     uid = new UniqueId(client, table, METRIC, 3, true);
735     final long id = 42L;
736     final byte[] id_array = { 0, 0, 0x2A };
737 
738     when(RandomUniqueId.getRandomUID()).thenReturn(24L).thenReturn(id);
739 
740     when(client.get(any(GetRequest.class)))
741       .thenReturn(Deferred.fromResult((ArrayList<KeyValue>)null));
742 
743     when(client.compareAndSet(anyPut(), any(byte[].class)))
744       .thenReturn(Deferred.fromResult(false))
745       .thenReturn(Deferred.fromResult(true))
746       .thenReturn(Deferred.fromResult(true));
747 
748     assertArrayEquals(id_array, uid.getOrCreateId("foo"));
749     // Should be a cache hit ...
750     assertArrayEquals(id_array, uid.getOrCreateId("foo"));
751     assertEquals(1, uid.cacheHits());
752     assertEquals(1, uid.cacheMisses());
753     assertEquals(2, uid.cacheSize());
754     assertEquals(1, uid.randomIdCollisions());
755 
756     // ... so verify there was only one HBase Get.
757     verify(client).get(anyGet());
758   }
759 
760   @Test
getOrCreateIdRandomCollisionTooManyAttempts()761   public void getOrCreateIdRandomCollisionTooManyAttempts() {
762     PowerMockito.mockStatic(RandomUniqueId.class);
763     uid = new UniqueId(client, table, METRIC, 3, true);
764     final long id = 42L;
765 
766     when(RandomUniqueId.getRandomUID()).thenReturn(24L).thenReturn(id);
767 
768     when(client.get(any(GetRequest.class)))
769       .thenReturn(Deferred.fromResult((ArrayList<KeyValue>)null));
770 
771     when(client.compareAndSet(any(PutRequest.class), any(byte[].class)))
772       .thenReturn(Deferred.fromResult(false))
773       .thenReturn(Deferred.fromResult(false))
774       .thenReturn(Deferred.fromResult(false))
775       .thenReturn(Deferred.fromResult(false))
776       .thenReturn(Deferred.fromResult(false))
777       .thenReturn(Deferred.fromResult(false))
778       .thenReturn(Deferred.fromResult(false))
779       .thenReturn(Deferred.fromResult(false))
780       .thenReturn(Deferred.fromResult(false))
781       .thenReturn(Deferred.fromResult(false));
782 
783     try {
784       final byte[] assigned_id = uid.getOrCreateId("foo");
785       fail("FailedToAssignUniqueIdException should have been thrown but instead "
786            + " this was returned id=" + Arrays.toString(assigned_id));
787     } catch (FailedToAssignUniqueIdException e) {
788       // OK
789     }
790     assertEquals(0, uid.cacheHits());
791     assertEquals(1, uid.cacheMisses());
792     assertEquals(0, uid.cacheSize());
793     assertEquals(9, uid.randomIdCollisions());
794 
795     // ... so verify there was only one HBase Get.
796     verify(client).get(any(GetRequest.class));
797   }
798 
799   @Test
getOrCreateIdRandomWithRaceCondition()800   public void getOrCreateIdRandomWithRaceCondition() {
801     PowerMockito.mockStatic(RandomUniqueId.class);
802     uid = new UniqueId(client, table, METRIC, 3, true);
803     final long id = 24L;
804     final byte[] id_array = { 0, 0, 0x2A };
805     final byte[] byte_name = { 'f', 'o', 'o' };
806 
807     ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
808     kvs.add(new KeyValue(byte_name, ID, METRIC_ARRAY, id_array));
809 
810     when(RandomUniqueId.getRandomUID()).thenReturn(id);
811 
812     when(client.get(any(GetRequest.class)))
813       .thenReturn(Deferred.fromResult((ArrayList<KeyValue>)null))
814       .thenReturn(Deferred.fromResult(kvs));
815 
816     when(client.compareAndSet(any(PutRequest.class), any(byte[].class)))
817       .thenReturn(Deferred.fromResult(true))
818       .thenReturn(Deferred.fromResult(false));
819 
820     assertArrayEquals(id_array, uid.getOrCreateId("foo"));
821     assertEquals(0, uid.cacheHits());
822     assertEquals(2, uid.cacheMisses());
823     assertEquals(2, uid.cacheSize());
824     assertEquals(1, uid.randomIdCollisions());
825 
826     // ... so verify there was only one HBase Get.
827     verify(client, times(2)).get(any(GetRequest.class));
828   }
829 
830   @Test
suggestWithNoMatch()831   public void suggestWithNoMatch() {
832     uid = new UniqueId(client, table, METRIC, 3);
833 
834     final Scanner fake_scanner = mock(Scanner.class);
835     when(client.newScanner(table))
836       .thenReturn(fake_scanner);
837 
838     when(fake_scanner.nextRows())
839       .thenReturn(Deferred.<ArrayList<ArrayList<KeyValue>>>fromResult(null));
840     // Watch this! ______,^   I'm writing C++ in Java!
841 
842     final List<String> suggestions = uid.suggest("nomatch");
843     assertEquals(0, suggestions.size());  // No results.
844 
845     verify(fake_scanner).setStartKey("nomatch".getBytes());
846     verify(fake_scanner).setStopKey("nomatci".getBytes());
847     verify(fake_scanner).setFamily(ID);
848     verify(fake_scanner).setQualifier(METRIC_ARRAY);
849   }
850 
851   @Test
suggestWithMatches()852   public void suggestWithMatches() {
853     uid = new UniqueId(client, table, METRIC, 3);
854 
855     final Scanner fake_scanner = mock(Scanner.class);
856     when(client.newScanner(table))
857       .thenReturn(fake_scanner);
858 
859     final ArrayList<ArrayList<KeyValue>> rows = new ArrayList<ArrayList<KeyValue>>(2);
860     final byte[] foo_bar_id = { 0, 0, 1 };
861     {
862       ArrayList<KeyValue> row = new ArrayList<KeyValue>(1);
863       row.add(new KeyValue("foo.bar".getBytes(), ID, METRIC_ARRAY, foo_bar_id));
864       rows.add(row);
865       row = new ArrayList<KeyValue>(1);
866       row.add(new KeyValue("foo.baz".getBytes(), ID, METRIC_ARRAY,
867                            new byte[] { 0, 0, 2 }));
868       rows.add(row);
869     }
870     when(fake_scanner.nextRows())
871       .thenReturn(Deferred.<ArrayList<ArrayList<KeyValue>>>fromResult(rows))
872       .thenReturn(Deferred.<ArrayList<ArrayList<KeyValue>>>fromResult(null));
873     // Watch this! ______,^   I'm writing C++ in Java!
874 
875     final List<String> suggestions = uid.suggest("foo");
876     final ArrayList<String> expected = new ArrayList<String>(2);
877     expected.add("foo.bar");
878     expected.add("foo.baz");
879     assertEquals(expected, suggestions);
880     // Verify that we cached the forward + backwards mapping for both results
881     // we "discovered" as a result of the scan.
882     assertEquals(4, uid.cacheSize());
883     assertEquals(0, uid.cacheHits());
884 
885     // Verify that the cached results are usable.
886     // Should be a cache hit ...
887     assertArrayEquals(foo_bar_id, uid.getOrCreateId("foo.bar"));
888     assertEquals(1, uid.cacheHits());
889     // ... so verify there was no HBase Get.
890     verify(client, never()).get(anyGet());
891   }
892 
893   @Test
uidToString()894   public void uidToString() {
895     assertEquals("01", UniqueId.uidToString(new byte[] { 1 }));
896   }
897 
898   @Test
uidToString2()899   public void uidToString2() {
900     assertEquals("0A0B", UniqueId.uidToString(new byte[] { 10, 11 }));
901   }
902 
903   @Test
uidToString3()904   public void uidToString3() {
905     assertEquals("1A1B", UniqueId.uidToString(new byte[] { 26, 27 }));
906   }
907 
908   @Test
uidToStringZeros()909   public void uidToStringZeros() {
910     assertEquals("00", UniqueId.uidToString(new byte[] { 0 }));
911   }
912 
913   @Test
uidToString255()914   public void uidToString255() {
915     assertEquals("FF", UniqueId.uidToString(new byte[] { (byte) 255 }));
916   }
917 
918   @Test (expected = NullPointerException.class)
uidToStringNull()919   public void uidToStringNull() {
920     UniqueId.uidToString(null);
921   }
922 
923   @Test
stringToUid()924   public void stringToUid() {
925     assertArrayEquals(new byte[] { 0x0a, 0x0b }, UniqueId.stringToUid("0A0B"));
926   }
927 
928   @Test
stringToUidNormalize()929   public void stringToUidNormalize() {
930     assertArrayEquals(new byte[] { (byte) 171 }, UniqueId.stringToUid("AB"));
931   }
932 
933   @Test
stringToUidCase()934   public void stringToUidCase() {
935     assertArrayEquals(new byte[] { (byte) 11 }, UniqueId.stringToUid("B"));
936   }
937 
938   @Test
stringToUidWidth()939   public void stringToUidWidth() {
940     assertArrayEquals(new byte[] { (byte) 0, (byte) 42, (byte) 12 },
941         UniqueId.stringToUid("2A0C", (short)3));
942   }
943 
944   @Test
stringToUidWidth2()945   public void stringToUidWidth2() {
946     assertArrayEquals(new byte[] { (byte) 0, (byte) 0, (byte) 0 },
947         UniqueId.stringToUid("0", (short)3));
948   }
949 
950   @Test (expected = IllegalArgumentException.class)
stringToUidNull()951   public void stringToUidNull() {
952     UniqueId.stringToUid(null);
953   }
954 
955   @Test (expected = IllegalArgumentException.class)
stringToUidEmpty()956   public void stringToUidEmpty() {
957     UniqueId.stringToUid("");
958   }
959 
960   @Test (expected = IllegalArgumentException.class)
stringToUidNotHex()961   public void stringToUidNotHex() {
962     UniqueId.stringToUid("HelloWorld");
963   }
964 
965   @Test (expected = IllegalArgumentException.class)
stringToUidNotHex2()966   public void stringToUidNotHex2() {
967     UniqueId.stringToUid(" ");
968   }
969 
970   @Test
getTSUIDFromKey()971   public void getTSUIDFromKey() {
972     final byte[] tsuid = UniqueId.getTSUIDFromKey(new byte[]
973       { 0, 0, 1, 1, 1, 1, 1, 0, 0, 2, 0, 0, 3 }, (short)3, (short)4);
974     assertArrayEquals(new byte[] { 0, 0, 1, 0, 0, 2, 0, 0, 3 },
975         tsuid);
976   }
977 
978   @Test
getTSUIDFromKeySalted()979   public void getTSUIDFromKeySalted() {
980     PowerMockito.mockStatic(Const.class);
981     PowerMockito.when(Const.SALT_WIDTH()).thenReturn(1);
982 
983     final byte[] expected = { 0, 0, 1, 0, 0, 2, 0, 0, 3 };
984     byte[] tsuid = UniqueId.getTSUIDFromKey(new byte[]
985       { 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 2, 0, 0, 3 }, (short)3, (short)4);
986     assertArrayEquals(expected, tsuid);
987 
988     tsuid = UniqueId.getTSUIDFromKey(new byte[]
989       { 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 2, 0, 0, 3 }, (short)3, (short)4);
990     assertArrayEquals(expected, tsuid);
991 
992     PowerMockito.when(Const.SALT_WIDTH()).thenReturn(4);
993     tsuid = UniqueId.getTSUIDFromKey(new byte[]
994       { 1, 2, 3, 4, 0, 0, 1, 1, 1, 1, 1, 0, 0, 2, 0, 0, 3 }, (short)3, (short)4);
995     assertArrayEquals(expected, tsuid);
996 
997     tsuid = UniqueId.getTSUIDFromKey(new byte[]
998       { 4, 3, 2, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 2, 0, 0, 3 }, (short)3, (short)4);
999     assertArrayEquals(expected, tsuid);
1000   }
1001 
1002   @Test (expected = IllegalArgumentException.class)
getTSUIDFromKeyMissingTags()1003   public void getTSUIDFromKeyMissingTags() {
1004     UniqueId.getTSUIDFromKey(new byte[]
1005       { 0, 0, 1, 1, 1, 1, 1 }, (short)3, (short)4);
1006   }
1007 
1008   @Test (expected = IllegalArgumentException.class)
getTSUIDFromKeyMissingTagsSalted()1009   public void getTSUIDFromKeyMissingTagsSalted() {
1010     PowerMockito.mockStatic(Const.class);
1011     PowerMockito.when(Const.SALT_WIDTH()).thenReturn(1);
1012 
1013     UniqueId.getTSUIDFromKey(new byte[]
1014       { 0, 0, 0, 1, 1, 1, 1, 1 }, (short)3, (short)4);
1015   }
1016 
1017   @Test (expected = IllegalArgumentException.class)
getTSUIDFromKeyMissingSalt()1018   public void getTSUIDFromKeyMissingSalt() {
1019     PowerMockito.mockStatic(Const.class);
1020     PowerMockito.when(Const.SALT_WIDTH()).thenReturn(1);
1021 
1022     UniqueId.getTSUIDFromKey(new byte[]
1023         { 0, 0, 1, 1, 1, 1, 1, 0, 0, 2, 0, 0, 3 }, (short)3, (short)4);
1024   }
1025 
1026   @Test (expected = IllegalArgumentException.class)
getTSUIDFromKeySaltButShouldntBe()1027   public void getTSUIDFromKeySaltButShouldntBe() {
1028     UniqueId.getTSUIDFromKey(new byte[]
1029         { 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 2, 0, 0, 3 }, (short)3, (short)4);
1030   }
1031 
1032   @Test
getTagPairsFromTSUIDString()1033   public void getTagPairsFromTSUIDString() {
1034     List<byte[]> tags = UniqueId.getTagPairsFromTSUID(
1035         "000000000001000002000003000004");
1036     assertNotNull(tags);
1037     assertEquals(2, tags.size());
1038     assertArrayEquals(new byte[] { 0, 0, 1, 0, 0, 2 }, tags.get(0));
1039     assertArrayEquals(new byte[] { 0, 0, 3, 0, 0, 4 }, tags.get(1));
1040   }
1041 
1042   @Test
getTagPairsFromTSUIDStringNonStandardWidth()1043   public void getTagPairsFromTSUIDStringNonStandardWidth() {
1044     PowerMockito.mockStatic(TSDB.class);
1045     when(TSDB.metrics_width()).thenReturn((short)3);
1046     when(TSDB.tagk_width()).thenReturn((short)4);
1047     when(TSDB.tagv_width()).thenReturn((short)3);
1048 
1049     List<byte[]> tags = UniqueId.getTagPairsFromTSUID(
1050         "0000000000000100000200000003000004");
1051     assertNotNull(tags);
1052     assertEquals(2, tags.size());
1053     assertArrayEquals(new byte[] { 0, 0, 0, 1, 0, 0, 2 }, tags.get(0));
1054     assertArrayEquals(new byte[] { 0, 0, 0, 3, 0, 0, 4 }, tags.get(1));
1055   }
1056 
1057   @Test (expected = IllegalArgumentException.class)
getTagPairsFromTSUIDStringMissingTags()1058   public void getTagPairsFromTSUIDStringMissingTags() {
1059     UniqueId.getTagPairsFromTSUID("123456");
1060   }
1061 
1062   @Test (expected = IllegalArgumentException.class)
getTagPairsFromTSUIDStringMissingMetric()1063   public void getTagPairsFromTSUIDStringMissingMetric() {
1064     UniqueId.getTagPairsFromTSUID("000001000002");
1065   }
1066 
1067   @Test (expected = IllegalArgumentException.class)
getTagPairsFromTSUIDStringOddNumberOfCharacters()1068   public void getTagPairsFromTSUIDStringOddNumberOfCharacters() {
1069     UniqueId.getTagPairsFromTSUID("0000080000010000020");
1070   }
1071 
1072   @Test (expected = IllegalArgumentException.class)
getTagPairsFromTSUIDStringMissingTagv()1073   public void getTagPairsFromTSUIDStringMissingTagv() {
1074     UniqueId.getTagPairsFromTSUID("000008000001");
1075   }
1076 
1077   @Test (expected = IllegalArgumentException.class)
getTagPairsFromTSUIDStringNull()1078   public void getTagPairsFromTSUIDStringNull() {
1079     UniqueId.getTagPairsFromTSUID((String)null);
1080   }
1081 
1082   @Test (expected = IllegalArgumentException.class)
getTagPairsFromTSUIDStringEmpty()1083   public void getTagPairsFromTSUIDStringEmpty() {
1084     UniqueId.getTagPairsFromTSUID("");
1085   }
1086 
1087   @Test
getTagPairsFromTSUIDBytes()1088   public void getTagPairsFromTSUIDBytes() {
1089     List<byte[]> tags = UniqueId.getTagPairsFromTSUID(
1090         new byte[] { 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4 });
1091     assertNotNull(tags);
1092     assertEquals(2, tags.size());
1093     assertArrayEquals(new byte[] { 0, 0, 1, 0, 0, 2 }, tags.get(0));
1094     assertArrayEquals(new byte[] { 0, 0, 3, 0, 0, 4 }, tags.get(1));
1095   }
1096 
1097   @Test
getTagPairsFromTSUIDBytesNonStandardWidth()1098   public void getTagPairsFromTSUIDBytesNonStandardWidth() {
1099     PowerMockito.mockStatic(TSDB.class);
1100     when(TSDB.metrics_width()).thenReturn((short)3);
1101     when(TSDB.tagk_width()).thenReturn((short)4);
1102     when(TSDB.tagv_width()).thenReturn((short)3);
1103 
1104     List<byte[]> tags = UniqueId.getTagPairsFromTSUID(
1105         new byte[] { 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 3, 0, 0, 4 });
1106     assertNotNull(tags);
1107     assertEquals(2, tags.size());
1108     assertArrayEquals(new byte[] { 0, 0, 0, 1, 0, 0, 2 }, tags.get(0));
1109     assertArrayEquals(new byte[] { 0, 0, 0, 3, 0, 0, 4 }, tags.get(1));
1110   }
1111 
1112   @Test (expected = IllegalArgumentException.class)
getTagPairsFromTSUIDBytesMissingTags()1113   public void getTagPairsFromTSUIDBytesMissingTags() {
1114     UniqueId.getTagPairsFromTSUID(new byte[] { 0, 0, 1 });
1115   }
1116 
1117   @Test (expected = IllegalArgumentException.class)
getTagPairsFromTSUIDBytesMissingMetric()1118   public void getTagPairsFromTSUIDBytesMissingMetric() {
1119     UniqueId.getTagPairsFromTSUID(new byte[] { 0, 0, 1, 0, 0, 2 });
1120   }
1121 
1122   @Test (expected = IllegalArgumentException.class)
getTagPairsFromTSUIDBytesMissingTagv()1123   public void getTagPairsFromTSUIDBytesMissingTagv() {
1124     UniqueId.getTagPairsFromTSUID(new byte[] { 0, 0, 8, 0, 0, 2 });
1125   }
1126 
1127   @Test (expected = IllegalArgumentException.class)
getTagPairsFromTSUIDBytesNull()1128   public void getTagPairsFromTSUIDBytesNull() {
1129     UniqueId.getTagPairsFromTSUID((byte[])null);
1130   }
1131 
1132   @Test (expected = IllegalArgumentException.class)
getTagPairsFromTSUIDBytesEmpty()1133   public void getTagPairsFromTSUIDBytesEmpty() {
1134     UniqueId.getTagPairsFromTSUID(new byte[0]);
1135   }
1136 
1137   @Test
getTagFromTSUID()1138   public void getTagFromTSUID() {
1139     List<byte[]> tags = UniqueId.getTagsFromTSUID(
1140         "000000000001000002000003000004");
1141     assertNotNull(tags);
1142     assertEquals(4, tags.size());
1143     assertArrayEquals(new byte[] { 0, 0, 1 }, tags.get(0));
1144     assertArrayEquals(new byte[] { 0, 0, 2 }, tags.get(1));
1145     assertArrayEquals(new byte[] { 0, 0, 3 }, tags.get(2));
1146     assertArrayEquals(new byte[] { 0, 0, 4 }, tags.get(3));
1147   }
1148 
1149   @Test
getTagFromTSUIDNonStandardWidth()1150   public void getTagFromTSUIDNonStandardWidth() {
1151     PowerMockito.mockStatic(TSDB.class);
1152     when(TSDB.metrics_width()).thenReturn((short)3);
1153     when(TSDB.tagk_width()).thenReturn((short)4);
1154     when(TSDB.tagv_width()).thenReturn((short)3);
1155 
1156     List<byte[]> tags = UniqueId.getTagsFromTSUID(
1157         "0000000000000100000200000003000004");
1158     assertNotNull(tags);
1159     assertEquals(4, tags.size());
1160     assertArrayEquals(new byte[] { 0, 0, 0, 1 }, tags.get(0));
1161     assertArrayEquals(new byte[] { 0, 0, 2 }, tags.get(1));
1162     assertArrayEquals(new byte[] { 0, 0, 0, 3 }, tags.get(2));
1163     assertArrayEquals(new byte[] { 0, 0, 4 }, tags.get(3));
1164   }
1165 
1166   @Test (expected = IllegalArgumentException.class)
getTagFromTSUIDMissingTags()1167   public void getTagFromTSUIDMissingTags() {
1168     UniqueId.getTagsFromTSUID("123456");
1169   }
1170 
1171   @Test (expected = IllegalArgumentException.class)
getTagFromTSUIDMissingMetric()1172   public void getTagFromTSUIDMissingMetric() {
1173     UniqueId.getTagsFromTSUID("000001000002");
1174   }
1175 
1176   @Test (expected = IllegalArgumentException.class)
getTagFromTSUIDOddNumberOfCharacters()1177   public void getTagFromTSUIDOddNumberOfCharacters() {
1178     UniqueId.getTagsFromTSUID("0000080000010000020");
1179   }
1180 
1181   @Test (expected = IllegalArgumentException.class)
getTagFromTSUIDMissingTagv()1182   public void getTagFromTSUIDMissingTagv() {
1183     UniqueId.getTagsFromTSUID("000008000001");
1184   }
1185 
1186   @Test (expected = IllegalArgumentException.class)
getTagFromTSUIDNull()1187   public void getTagFromTSUIDNull() {
1188     UniqueId.getTagsFromTSUID(null);
1189   }
1190 
1191   @Test (expected = IllegalArgumentException.class)
getTagFromTSUIDEmpty()1192   public void getTagFromTSUIDEmpty() {
1193     UniqueId.getTagsFromTSUID("");
1194   }
1195 
1196   @Test
getUsedUIDs()1197   public void getUsedUIDs() throws Exception {
1198     final ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(3);
1199     final byte[] metrics = { 'm', 'e', 't', 'r', 'i', 'c', 's' };
1200     final byte[] tagk = { 't', 'a', 'g', 'k' };
1201     final byte[] tagv = { 't', 'a', 'g', 'v' };
1202     kvs.add(new KeyValue(MAXID, ID, metrics, Bytes.fromLong(64L)));
1203     kvs.add(new KeyValue(MAXID, ID, tagk, Bytes.fromLong(42L)));
1204     kvs.add(new KeyValue(MAXID, ID, tagv, Bytes.fromLong(1024L)));
1205     final TSDB tsdb = mock(TSDB.class);
1206     when(tsdb.getClient()).thenReturn(client);
1207     when(tsdb.uidTable()).thenReturn(new byte[] { 'u', 'i', 'd' });
1208     when(client.get(anyGet()))
1209       .thenReturn(Deferred.fromResult(kvs));
1210 
1211     final byte[][] kinds = { metrics, tagk, tagv };
1212     final Map<String, Long> uids = UniqueId.getUsedUIDs(tsdb, kinds)
1213       .joinUninterruptibly();
1214     assertNotNull(uids);
1215     assertEquals(3, uids.size());
1216     assertEquals(64L, uids.get("metrics").longValue());
1217     assertEquals(42L, uids.get("tagk").longValue());
1218     assertEquals(1024L, uids.get("tagv").longValue());
1219   }
1220 
1221   @Test
getUsedUIDsEmptyRow()1222   public void getUsedUIDsEmptyRow() throws Exception {
1223     final ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(0);
1224     final byte[] metrics = { 'm', 'e', 't', 'r', 'i', 'c', 's' };
1225     final byte[] tagk = { 't', 'a', 'g', 'k' };
1226     final byte[] tagv = { 't', 'a', 'g', 'v' };
1227     final TSDB tsdb = mock(TSDB.class);
1228     when(tsdb.getClient()).thenReturn(client);
1229     when(tsdb.uidTable()).thenReturn(new byte[] { 'u', 'i', 'd' });
1230     when(client.get(anyGet()))
1231       .thenReturn(Deferred.fromResult(kvs));
1232 
1233     final byte[][] kinds = { metrics, tagk, tagv };
1234     final Map<String, Long> uids = UniqueId.getUsedUIDs(tsdb, kinds)
1235       .joinUninterruptibly();
1236     assertNotNull(uids);
1237     assertEquals(3, uids.size());
1238     assertEquals(0L, uids.get("metrics").longValue());
1239     assertEquals(0L, uids.get("tagk").longValue());
1240     assertEquals(0L, uids.get("tagv").longValue());
1241   }
1242 
1243   @Test
uidToLong()1244   public void uidToLong() throws Exception {
1245     assertEquals(42, UniqueId.uidToLong(new byte[] { 0, 0, 0x2A }, (short)3));
1246   }
1247 
1248   @Test
uidToLongFromString()1249   public void uidToLongFromString() throws Exception {
1250     assertEquals(42L, UniqueId.uidToLong("00002A", (short) 3));
1251   }
1252 
1253   @Test (expected = IllegalArgumentException.class)
uidToLongTooLong()1254   public void uidToLongTooLong() throws Exception {
1255     UniqueId.uidToLong(new byte[] { 0, 0, 0, 0x2A }, (short)3);
1256   }
1257 
1258   @Test (expected = IllegalArgumentException.class)
uidToLongTooShort()1259   public void uidToLongTooShort() throws Exception {
1260     UniqueId.uidToLong(new byte[] { 0, 0x2A }, (short)3);
1261   }
1262 
1263   @Test (expected = NullPointerException.class)
uidToLongNull()1264   public void uidToLongNull() throws Exception {
1265     UniqueId.uidToLong((byte[])null, (short)3);
1266   }
1267 
1268   @Test
longToUID()1269   public void longToUID() throws Exception {
1270     assertArrayEquals(new byte[] { 0, 0, 0x2A },
1271         UniqueId.longToUID(42L, (short)3));
1272   }
1273 
1274   @Test (expected = IllegalStateException.class)
longToUIDTooBig()1275   public void longToUIDTooBig() throws Exception {
1276     UniqueId.longToUID(257, (short)1);
1277   }
1278 
1279   @Test
rename()1280   public void rename() throws Exception {
1281     uid = new UniqueId(client, table, METRIC, 3);
1282     final byte[] foo_id = { 0, 'a', 0x42 };
1283     final byte[] foo_name = { 'f', 'o', 'o' };
1284 
1285     ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
1286     kvs.add(new KeyValue(foo_name, ID, METRIC_ARRAY, foo_id));
1287     when(client.get(anyGet()))
1288         .thenReturn(Deferred.fromResult(kvs))
1289         .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
1290     when(client.put(anyPut())).thenAnswer(answerTrue());
1291     when(client.delete(anyDelete())).thenAnswer(answerTrue());
1292 
1293     uid.rename("foo", "bar");
1294   }
1295 
1296   @Test (expected = IllegalArgumentException.class)
renameNewNameExists()1297   public void renameNewNameExists() throws Exception {
1298     uid = new UniqueId(client, table, METRIC, 3);
1299     final byte[] foo_id = { 0, 'a', 0x42 };
1300     final byte[] foo_name = { 'f', 'o', 'o' };
1301     final byte[] bar_id = { 1, 'b', 0x43 };
1302     final byte[] bar_name = { 'b', 'a', 'r' };
1303 
1304     ArrayList<KeyValue> foo_kvs = new ArrayList<KeyValue>(1);
1305     ArrayList<KeyValue> bar_kvs = new ArrayList<KeyValue>(1);
1306     foo_kvs.add(new KeyValue(foo_name, ID, METRIC_ARRAY, foo_id));
1307     bar_kvs.add(new KeyValue(bar_name, ID, METRIC_ARRAY, bar_id));
1308     when(client.get(anyGet()))
1309         .thenReturn(Deferred.fromResult(foo_kvs))
1310         .thenReturn(Deferred.fromResult(bar_kvs));
1311     when(client.put(anyPut())).thenAnswer(answerTrue());
1312     when(client.delete(anyDelete())).thenAnswer(answerTrue());
1313 
1314     uid.rename("foo", "bar");
1315   }
1316 
1317   @Test (expected = IllegalStateException.class)
renameRaceCondition()1318   public void renameRaceCondition() throws Exception {
1319     // Simulate a race between client A(default) and client B.
1320     // A and B rename same UID to different name.
1321     // B waits till A start to invoke PutRequest to start.
1322 
1323     uid = new UniqueId(client, table, METRIC, 3);
1324     HBaseClient client_b = mock(HBaseClient.class);
1325     final UniqueId uid_b = new UniqueId(client_b, table, METRIC, 3);
1326 
1327     final byte[] foo_id = { 0, 'a', 0x42 };
1328     final byte[] foo_name = { 'f', 'o', 'o' };
1329 
1330     ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
1331     kvs.add(new KeyValue(foo_name, ID, METRIC_ARRAY, foo_id));
1332 
1333     when(client_b.get(anyGet()))
1334         .thenReturn(Deferred.fromResult(kvs))
1335         .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
1336     when(client_b.put(anyPut())).thenAnswer(answerTrue());
1337     when(client_b.delete(anyDelete())).thenAnswer(answerTrue());
1338 
1339     final Answer<Deferred<Boolean>> the_race = new Answer<Deferred<Boolean>>() {
1340       public Deferred<Boolean> answer(final InvocationOnMock inv) throws Exception {
1341         uid_b.rename("foo", "xyz");
1342         return Deferred.fromResult(true);
1343       }
1344     };
1345 
1346     when(client.get(anyGet()))
1347         .thenReturn(Deferred.fromResult(kvs))
1348         .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
1349     when(client.put(anyPut())).thenAnswer(the_race);
1350     when(client.delete(anyDelete())).thenAnswer(answerTrue());
1351 
1352     uid.rename("foo", "bar");
1353   }
1354 
1355   @Test
deleteCached()1356   public void deleteCached() throws Exception {
1357     setupStorage();
1358     uid = new UniqueId(client, table, METRIC, 3);
1359     uid.setTSDB(tsdb);
1360     assertArrayEquals(UID, uid.getId("sys.cpu.user"));
1361     assertEquals("sys.cpu.user", uid.getName(UID));
1362 
1363     uid.deleteAsync("sys.cpu.user").join();
1364     try {
1365       uid.getId("sys.cpu.user");
1366       fail("Expected a NoSuchUniqueName");
1367     } catch (NoSuchUniqueName nsun) { }
1368 
1369     try {
1370       uid.getName(UID);
1371       fail("Expected a NoSuchUniqueId");
1372     } catch (NoSuchUniqueId nsui) { }
1373 
1374     uid = new UniqueId(client, table, TAGK, 3);
1375     uid.setTSDB(tsdb);
1376     assertArrayEquals(UID, uid.getId("host"));
1377     assertEquals("host", uid.getName(UID));
1378 
1379     uid = new UniqueId(client, table, TAGV, 3);
1380     uid.setTSDB(tsdb);
1381     assertArrayEquals(UID, uid.getId("web01"));
1382     assertEquals("web01", uid.getName(UID));
1383   }
1384 
1385   @Test
deleteNotCached()1386   public void deleteNotCached() throws Exception {
1387     setupStorage();
1388     uid = new UniqueId(client, table, METRIC, 3);
1389     uid.setTSDB(tsdb);
1390     uid.deleteAsync("sys.cpu.user").join();
1391     try {
1392       uid.getId("sys.cpu.user");
1393       fail("Expected a NoSuchUniqueName");
1394     } catch (NoSuchUniqueName nsun) { }
1395 
1396     try {
1397       uid.getName(UID);
1398       fail("Expected a NoSuchUniqueId");
1399     } catch (NoSuchUniqueId nsui) { }
1400 
1401     uid = new UniqueId(client, table, TAGK, 3);
1402     uid.setTSDB(tsdb);
1403     assertArrayEquals(UID, uid.getId("host"));
1404     assertEquals("host", uid.getName(UID));
1405 
1406     uid = new UniqueId(client, table, TAGV, 3);
1407     uid.setTSDB(tsdb);
1408     assertArrayEquals(UID, uid.getId("web01"));
1409     assertEquals("web01", uid.getName(UID));
1410   }
1411 
1412   @Test
deleteFailForwardDelete()1413   public void deleteFailForwardDelete() throws Exception {
1414     setupStorage();
1415     uid = new UniqueId(client, table, METRIC, 3);
1416     uid.setTSDB(tsdb);
1417     assertArrayEquals(UID, uid.getId("sys.cpu.user"));
1418     assertEquals("sys.cpu.user", uid.getName(UID));
1419 
1420     storage.throwException("sys.cpu.user".getBytes(), fakeHBaseException());
1421     try {
1422       uid.deleteAsync("sys.cpu.user").join();
1423       fail("Expected HBaseException");
1424     } catch (HBaseException e) { }
1425     catch (Exception e) { }
1426     storage.clearExceptions();
1427     try {
1428       uid.getName(UID);
1429       fail("Expected a NoSuchUniqueId");
1430     } catch (NoSuchUniqueId nsui) { }
1431     assertArrayEquals(UID, uid.getId("sys.cpu.user"));
1432     // now it pollutes the cache
1433     assertEquals("sys.cpu.user", uid.getName(UID));
1434   }
1435 
1436   @Test
deleteFailReverseDelete()1437   public void deleteFailReverseDelete() throws Exception {
1438     setupStorage();
1439     storage.throwException(UID, fakeHBaseException());
1440     uid = new UniqueId(client, table, METRIC, 3);
1441     uid.setTSDB(tsdb);
1442     try {
1443       uid.deleteAsync("sys.cpu.user").join();
1444       fail("Expected HBaseException");
1445     } catch (HBaseException e) { }
1446     catch (Exception e) { }
1447     storage.clearExceptions();
1448     try {
1449       uid.getId("sys.cpu.user");
1450       fail("Expected a NoSuchUniqueName");
1451     } catch (NoSuchUniqueName nsun) { }
1452     assertEquals("sys.cpu.user", uid.getName(UID));
1453   }
1454 
1455   @Test
deleteNoSuchUniqueName()1456   public void deleteNoSuchUniqueName() throws Exception {
1457     setupStorage();
1458     uid = new UniqueId(client, table, METRIC, 3);
1459     uid.setTSDB(tsdb);
1460     storage.flushRow(table, "sys.cpu.user".getBytes());
1461     try {
1462       uid.deleteAsync("sys.cpu.user").join();
1463       fail("Expected NoSuchUniqueName");
1464     } catch (NoSuchUniqueName e) { }
1465     assertEquals("sys.cpu.user", uid.getName(UID));
1466   }
1467 
1468   // ----------------- //
1469   // Helper functions. //
1470   // ----------------- //
1471 
setupStorage()1472   private void setupStorage() throws Exception {
1473     final Config config = mock(Config.class);
1474     when(tsdb.getConfig()).thenReturn(config);
1475     when(tsdb.getClient()).thenReturn(client);
1476     storage = new MockBase(tsdb, client, true, true, true, true);
1477 
1478     final List<byte[]> families = new ArrayList<byte[]>();
1479     families.add(ID);
1480     families.add(NAME);
1481     storage.addTable(table, families);
1482 
1483     storage.addColumn(table, "sys.cpu.user".getBytes(), ID, METRIC_ARRAY, UID);
1484     storage.addColumn(table, UID, NAME, METRIC_ARRAY, "sys.cpu.user".getBytes());
1485     storage.addColumn(table, "host".getBytes(), ID, TAGK_ARRAY, UID);
1486     storage.addColumn(table, UID, NAME, TAGK_ARRAY, "host".getBytes());
1487     storage.addColumn(table, "web01".getBytes(), ID, TAGV_ARRAY, UID);
1488     storage.addColumn(table, UID, NAME,TAGV_ARRAY, "web01".getBytes());
1489   }
1490 
emptyArray()1491   private static byte[] emptyArray() {
1492     return eq(HBaseClient.EMPTY_ARRAY);
1493   }
1494 
anyGet()1495   private static GetRequest anyGet() {
1496     return any(GetRequest.class);
1497   }
1498 
incrementForRow(final byte[] row)1499   private static AtomicIncrementRequest incrementForRow(final byte[] row) {
1500     return argThat(new ArgumentMatcher<AtomicIncrementRequest>() {
1501       public boolean matches(Object incr) {
1502         return Arrays.equals(((AtomicIncrementRequest) incr).key(), row);
1503       }
1504       public void describeTo(org.hamcrest.Description description) {
1505         description.appendText("AtomicIncrementRequest for row "
1506                                + Arrays.toString(row));
1507       }
1508     });
1509   }
1510 
1511   private static PutRequest anyPut() {
1512     return any(PutRequest.class);
1513   }
1514 
1515   private static DeleteRequest anyDelete() {
1516     return any(DeleteRequest.class);
1517   }
1518 
1519   private static Answer<Deferred<Boolean>> answerTrue() {
1520     return new Answer<Deferred<Boolean>>() {
1521       public Deferred<Boolean> answer(final InvocationOnMock inv) {
1522         return Deferred.fromResult(true);
1523       }
1524     };
1525   }
1526 
1527   @SuppressWarnings("unchecked")
1528   private static Callback<byte[], ArrayList<KeyValue>> anyByteCB() {
1529     return any(Callback.class);
1530   }
1531 
1532   private static PutRequest putForRow(final byte[] row) {
1533     return argThat(new ArgumentMatcher<PutRequest>() {
1534       public boolean matches(Object put) {
1535         return Arrays.equals(((PutRequest) put).key(), row);
1536       }
1537       public void describeTo(org.hamcrest.Description description) {
1538         description.appendText("PutRequest for row " + Arrays.toString(row));
1539       }
1540     });
1541   }
1542 
1543   private static HBaseException fakeHBaseException() {
1544     final HBaseException hbe = mock(HBaseException.class);
1545     when(hbe.getStackTrace())
1546       // Truncate the stack trace because otherwise it's gigantic.
1547       .thenReturn(Arrays.copyOf(new RuntimeException().getStackTrace(), 3));
1548     when(hbe.getMessage())
1549       .thenReturn("fake exception");
1550     return hbe;
1551   }
1552 
1553   private static final byte[] MAXID = { 0 };
1554 
1555 }
1556