Package net.opentsdb.uid

Source Code of net.opentsdb.uid.TestUniqueId

// This file is part of OpenTSDB.
// Copyright (C) 2010-2012  The OpenTSDB Authors.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 2.1 of the License, or (at your
// option) any later version.  This program is distributed in the hope that it
// will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
// General Public License for more details.  You should have received a copy
// of the GNU Lesser General Public License along with this program.  If not,
// see <http://www.gnu.org/licenses/>.
package net.opentsdb.uid;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import com.stumbleupon.async.Callback;
import com.stumbleupon.async.Deferred;

import net.opentsdb.core.TSDB;
import net.opentsdb.utils.Config;

import org.hbase.async.AtomicIncrementRequest;
import org.hbase.async.Bytes;
import org.hbase.async.GetRequest;
import org.hbase.async.HBaseClient;
import org.hbase.async.HBaseException;
import org.hbase.async.KeyValue;
import org.hbase.async.PutRequest;
import org.hbase.async.Scanner;

import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;

import org.mockito.ArgumentMatcher;
import org.mockito.InOrder;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.powermock.api.mockito.PowerMockito.mock;

@RunWith(PowerMockRunner.class)
// "Classloader hell"...  It's real.  Tell PowerMock to ignore these classes
// because they fiddle with the class loader.  We don't test them anyway.
@PowerMockIgnore({"javax.management.*", "javax.xml.*",
                  "ch.qos.*", "org.slf4j.*",
                  "com.sum.*", "org.xml.*"})
@PrepareForTest({ HBaseClient.class, TSDB.class, Config.class })
public final class TestUniqueId {

  private HBaseClient client = mock(HBaseClient.class);
  private static final byte[] table = { 't', 'a', 'b', 'l', 'e' };
  private static final byte[] ID = { 'i', 'd' };
  private UniqueId uid;
  private static final String kind = "metric";
  private static final byte[] kind_array = { 'm', 'e', 't', 'r', 'i', 'c' };

  @Test(expected=IllegalArgumentException.class)
  public void testCtorZeroWidth() {
    uid = new UniqueId(client, table, kind, 0);
  }

  @Test(expected=IllegalArgumentException.class)
  public void testCtorNegativeWidth() {
    uid = new UniqueId(client, table, kind, -1);
  }

  @Test(expected=IllegalArgumentException.class)
  public void testCtorEmptyKind() {
    uid = new UniqueId(client, table, "", 3);
  }

  @Test(expected=IllegalArgumentException.class)
  public void testCtorLargeWidth() {
    uid = new UniqueId(client, table, kind, 9);
  }

  @Test
  public void kindEqual() {
    uid = new UniqueId(client, table, kind, 3);
    assertEquals(kind, uid.kind());
  }

  @Test
  public void widthEqual() {
    uid = new UniqueId(client, table, kind, 3);
    assertEquals(3, uid.width());
  }

  @Test
  public void testMaxPossibleId() {
    assertEquals(255, (new UniqueId(client, table, kind, 1)).maxPossibleId());
    assertEquals(65535, (new UniqueId(client, table, kind, 2)).maxPossibleId());
    assertEquals(16777215L, (new UniqueId(client, table, kind, 3)).maxPossibleId());
  }
 
  @Test
  public void getNameSuccessfulHBaseLookup() {
    uid = new UniqueId(client, table, kind, 3);
    final byte[] id = { 0, 'a', 0x42 };
    final byte[] byte_name = { 'f', 'o', 'o' };

    ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
    kvs.add(new KeyValue(id, ID, kind_array, byte_name));
    when(client.get(anyGet()))
      .thenReturn(Deferred.fromResult(kvs));

    assertEquals("foo", uid.getName(id));
    // Should be a cache hit ...
    assertEquals("foo", uid.getName(id));

    assertEquals(1, uid.cacheHits());
    assertEquals(1, uid.cacheMisses());
    assertEquals(2, uid.cacheSize());

    // ... so verify there was only one HBase Get.
    verify(client).get(anyGet());
  }

  @Test
  public void getNameWithErrorDuringHBaseLookup() {
    uid = new UniqueId(client, table, kind, 3);
    final byte[] id = { 0, 'a', 0x42 };
    final byte[] byte_name = { 'f', 'o', 'o' };

    HBaseException hbe = mock(HBaseException.class);

    ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
    kvs.add(new KeyValue(id, ID, kind_array, byte_name));
    when(client.get(anyGet()))
      .thenThrow(hbe)
      .thenReturn(Deferred.fromResult(kvs));

    // 1st calls fails.
    try {
      uid.getName(id);
      fail("HBaseException should have been thrown.");
    } catch (HBaseException e) {
      assertSame(hbe, e)// OK.
    }

    // 2nd call succeeds.
    assertEquals("foo", uid.getName(id));

    assertEquals(0, uid.cacheHits());
    assertEquals(2, uid.cacheMisses())// 1st (failed) attempt + 2nd.
    assertEquals(2, uid.cacheSize());

    verify(client, times(2)).get(anyGet());
  }

  @Test(expected=NoSuchUniqueId.class)
  public void getNameForNonexistentId() {
    uid = new UniqueId(client, table, kind, 3);

    when(client.get(anyGet()))
      .thenReturn(Deferred.fromResult(new ArrayList<KeyValue>(0)));

    uid.getName(new byte[] { 1, 2, 3 });
  }

  @Test(expected=IllegalArgumentException.class)
  public void getNameWithInvalidId() {
    uid = new UniqueId(client, table, kind, 3);

    uid.getName(new byte[] { 1 });
  }

  @Test
  public void getIdSuccessfulHBaseLookup() {
    uid = new UniqueId(client, table, kind, 3);
    final byte[] id = { 0, 'a', 0x42 };
    final byte[] byte_name = { 'f', 'o', 'o' };

    ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
    kvs.add(new KeyValue(byte_name, ID, kind_array, id));
    when(client.get(anyGet()))
      .thenReturn(Deferred.fromResult(kvs));

    assertArrayEquals(id, uid.getId("foo"));
    // Should be a cache hit ...
    assertArrayEquals(id, uid.getId("foo"));
    // Should be a cache hit too ...
    assertArrayEquals(id, uid.getId("foo"));

    assertEquals(2, uid.cacheHits());
    assertEquals(1, uid.cacheMisses());
    assertEquals(2, uid.cacheSize());

    // ... so verify there was only one HBase Get.
    verify(client).get(anyGet());
  }

  // The table contains IDs encoded on 2 bytes but the instance wants 3.
  @Test(expected=IllegalStateException.class)
  public void getIdMisconfiguredWidth() {
    uid = new UniqueId(client, table, kind, 3);
    final byte[] id = { 'a', 0x42 };
    final byte[] byte_name = { 'f', 'o', 'o' };

    ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
    kvs.add(new KeyValue(byte_name, ID, kind_array, id));
    when(client.get(anyGet()))
      .thenReturn(Deferred.fromResult(kvs));

    uid.getId("foo");
  }

  @Test(expected=NoSuchUniqueName.class)
  public void getIdForNonexistentName() {
    uid = new UniqueId(client, table, kind, 3);

    when(client.get(anyGet()))      // null  =>  ID doesn't exist.
      .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
    // Watch this! ______,^   I'm writing C++ in Java!

    uid.getId("foo");
  }

  @Test
  public void getOrCreateIdWithExistingId() {
    uid = new UniqueId(client, table, kind, 3);
    final byte[] id = { 0, 'a', 0x42 };
    final byte[] byte_name = { 'f', 'o', 'o' };

    ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
    kvs.add(new KeyValue(byte_name, ID, kind_array, id));
    when(client.get(anyGet()))
      .thenReturn(Deferred.fromResult(kvs));

    assertArrayEquals(id, uid.getOrCreateId("foo"));
    // Should be a cache hit ...
    assertArrayEquals(id, uid.getOrCreateId("foo"));
    assertEquals(1, uid.cacheHits());
    assertEquals(1, uid.cacheMisses());
    assertEquals(2, uid.cacheSize());

    // ... so verify there was only one HBase Get.
    verify(client).get(anyGet());
  }

  @Test  // Test the creation of an ID with no problem.
  public void getOrCreateIdAssignIdWithSuccess() {
    uid = new UniqueId(client, table, kind, 3);
    final byte[] id = { 0, 0, 5 };
    final Config config = mock(Config.class);
    when(config.enable_realtime_uid()).thenReturn(false);
    final TSDB tsdb = mock(TSDB.class);
    when(tsdb.getConfig()).thenReturn(config);
    uid.setTSDB(tsdb);
   
    when(client.get(anyGet()))      // null  =>  ID doesn't exist.
      .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
    // Watch this! ______,^   I'm writing C++ in Java!

    when(client.atomicIncrement(incrementForRow(MAXID)))
      .thenReturn(Deferred.fromResult(5L));

    when(client.compareAndSet(anyPut(), emptyArray()))
      .thenReturn(Deferred.fromResult(true))
      .thenReturn(Deferred.fromResult(true));

    assertArrayEquals(id, uid.getOrCreateId("foo"));
    // Should be a cache hit since we created that entry.
    assertArrayEquals(id, uid.getOrCreateId("foo"));
    // Should be a cache hit too for the same reason.
    assertEquals("foo", uid.getName(id));

    verify(client).get(anyGet()); // Initial Get.
    verify(client).atomicIncrement(incrementForRow(MAXID));
    // Reverse + forward mappings.
    verify(client, times(2)).compareAndSet(anyPut(), emptyArray());
  }

  @PrepareForTest({HBaseClient.class, UniqueId.class})
  @Test  // Test the creation of an ID when unable to increment MAXID
  public void getOrCreateIdUnableToIncrementMaxId() throws Exception {
    PowerMockito.mockStatic(Thread.class);

    uid = new UniqueId(client, table, kind, 3);

    when(client.get(anyGet()))      // null  =>  ID doesn't exist.
      .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
    // Watch this! ______,^   I'm writing C++ in Java!

    HBaseException hbe = fakeHBaseException();
    when(client.atomicIncrement(incrementForRow(MAXID)))
      .thenThrow(hbe);
    PowerMockito.doNothing().when(Thread.class); Thread.sleep(anyInt());

    try {
      uid.getOrCreateId("foo");
      fail("HBaseException should have been thrown!");
    } catch (HBaseException e) {
      assertSame(hbe, e);
    }
  }

  @Test  // Test the creation of an ID with a race condition.
  @PrepareForTest({HBaseClient.class, Deferred.class})
   public void getOrCreateIdAssignIdWithRaceCondition() {
    // Simulate a race between client A and client B.
    // A does a Get and sees that there's no ID for this name.
    // B does a Get and sees that there's no ID too, and B actually goes
    // through the entire process to create the ID.
    // Then A attempts to go through the process and should discover that the
    // ID has already been assigned.

    uid = new UniqueId(client, table, kind, 3); // Used by client A.
    HBaseClient client_b = mock(HBaseClient.class); // For client B.
    final UniqueId uid_b = new UniqueId(client_b, table, kind, 3);

    final byte[] id = { 0, 0, 5 };
    final byte[] byte_name = { 'f', 'o', 'o' };
    final ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
    kvs.add(new KeyValue(byte_name, ID, kind_array, id));

    @SuppressWarnings("unchecked")
    final Deferred<ArrayList<KeyValue>> d = PowerMockito.spy(new Deferred<ArrayList<KeyValue>>());
    when(client.get(anyGet()))
      .thenReturn(d)
      .thenReturn(Deferred.fromResult(kvs));

    final Answer<byte[]> the_race = new Answer<byte[]>() {
      public byte[] answer(final InvocationOnMock unused_invocation) throws Exception {
        // While answering A's first Get, B doest a full getOrCreateId.
        assertArrayEquals(id, uid_b.getOrCreateId("foo"));
        d.callback(null);
        return (byte[]) ((Deferred) d).join();
      }
    };

    // Start the race when answering A's first Get.
    try {
      PowerMockito.doAnswer(the_race).when(d).joinUninterruptibly();
    } catch (Exception e) {
      fail("Should never happen: " + e);
    }

    when(client_b.get(anyGet())) // null => ID doesn't exist.
      .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
    // Watch this! ______,^ I'm writing C++ in Java!

    when(client_b.atomicIncrement(incrementForRow(MAXID)))
      .thenReturn(Deferred.fromResult(5L));

    when(client_b.compareAndSet(anyPut(), emptyArray()))
      .thenReturn(Deferred.fromResult(true))
      .thenReturn(Deferred.fromResult(true));

    // Now that B is finished, A proceeds and allocates a UID that will be
    // wasted, and creates the reverse mapping, but fails at creating the
    // forward mapping.
    when(client.atomicIncrement(incrementForRow(MAXID)))
      .thenReturn(Deferred.fromResult(6L));

    when(client.compareAndSet(anyPut(), emptyArray()))
      .thenReturn(Deferred.fromResult(true)) // Orphan reverse mapping.
      .thenReturn(Deferred.fromResult(false)); // Already CAS'ed by A.

    // Start the execution.
    assertArrayEquals(id, uid.getOrCreateId("foo"));

    // Verify the order of execution too.
    final InOrder order = inOrder(client, client_b);
    order.verify(client).get(anyGet()); // 1st Get for A.
    order.verify(client_b).get(anyGet()); // 1st Get for B.
    order.verify(client_b).atomicIncrement(incrementForRow(MAXID));
    order.verify(client_b, times(2)).compareAndSet(anyPut(), // both mappings.
                                                   emptyArray());
    order.verify(client).atomicIncrement(incrementForRow(MAXID));
    order.verify(client, times(2)).compareAndSet(anyPut(), // both mappings.
                                                 emptyArray());
    order.verify(client).get(anyGet()); // A retries and gets it.
  }

  @Test
  // Test the creation of an ID when all possible IDs are already in use
  public void getOrCreateIdWithOverflow() {
    uid = new UniqueId(client, table, kind, 1)// IDs are only on 1 byte.

    when(client.get(anyGet()))      // null  =>  ID doesn't exist.
      .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
    // Watch this! ______,^   I'm writing C++ in Java!

    // Update once HBASE-2292 is fixed:
    when(client.atomicIncrement(incrementForRow(MAXID)))
      .thenReturn(Deferred.fromResult(256L));

    try {
      final byte[] id = uid.getOrCreateId("foo");
      fail("IllegalArgumentException should have been thrown but instead "
           + " this was returned id=" + Arrays.toString(id));
    } catch (IllegalStateException e) {
      // OK.
    }

    verify(client, times(1)).get(anyGet())// Initial Get.
    verify(client).atomicIncrement(incrementForRow(MAXID));
  }

  @Test  // ICV throws an exception, we can't get an ID.
  public void getOrCreateIdWithICVFailure() {
    uid = new UniqueId(client, table, kind, 3);
    final Config config = mock(Config.class);
    when(config.enable_realtime_uid()).thenReturn(false);
    final TSDB tsdb = mock(TSDB.class);
    when(tsdb.getConfig()).thenReturn(config);
    uid.setTSDB(tsdb);
   
    when(client.get(anyGet()))      // null  =>  ID doesn't exist.
      .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
    // Watch this! ______,^   I'm writing C++ in Java!

    // Update once HBASE-2292 is fixed:
    HBaseException hbe = fakeHBaseException();
    when(client.atomicIncrement(incrementForRow(MAXID)))
      .thenReturn(Deferred.<Long>fromError(hbe))
      .thenReturn(Deferred.fromResult(5L));

    when(client.compareAndSet(anyPut(), emptyArray()))
      .thenReturn(Deferred.fromResult(true))
      .thenReturn(Deferred.fromResult(true));

    final byte[] id = { 0, 0, 5 };
    assertArrayEquals(id, uid.getOrCreateId("foo"));
    verify(client, times(1)).get(anyGet()); // Initial Get.
    // First increment (failed) + retry.
    verify(client, times(2)).atomicIncrement(incrementForRow(MAXID));
    // Reverse + forward mappings.
    verify(client, times(2)).compareAndSet(anyPut(), emptyArray());
  }

  @Test  // Test that the reverse mapping is created before the forward one.
  public void getOrCreateIdPutsReverseMappingFirst() {
    uid = new UniqueId(client, table, kind, 3);
    final Config config = mock(Config.class);
    when(config.enable_realtime_uid()).thenReturn(false);
    final TSDB tsdb = mock(TSDB.class);
    when(tsdb.getConfig()).thenReturn(config);
    uid.setTSDB(tsdb);
   
    when(client.get(anyGet()))      // null  =>  ID doesn't exist.
      .thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
    // Watch this! ______,^   I'm writing C++ in Java!

    when(client.atomicIncrement(incrementForRow(MAXID)))
      .thenReturn(Deferred.fromResult(6L));

    when(client.compareAndSet(anyPut(), emptyArray()))
      .thenReturn(Deferred.fromResult(true))
      .thenReturn(Deferred.fromResult(true));

    final byte[] id = { 0, 0, 6 };
    final byte[] row = { 'f', 'o', 'o' };
    assertArrayEquals(id, uid.getOrCreateId("foo"));

    final InOrder order = inOrder(client);
    order.verify(client).get(anyGet());            // Initial Get.
    order.verify(client).atomicIncrement(incrementForRow(MAXID));
    order.verify(client).compareAndSet(putForRow(id), emptyArray());
    order.verify(client).compareAndSet(putForRow(row), emptyArray());
  }

  @PrepareForTest({HBaseClient.class, Scanner.class})
  @Test
  public void suggestWithNoMatch() {
    uid = new UniqueId(client, table, kind, 3);

    final Scanner fake_scanner = mock(Scanner.class);
    when(client.newScanner(table))
      .thenReturn(fake_scanner);

    when(fake_scanner.nextRows())
      .thenReturn(Deferred.<ArrayList<ArrayList<KeyValue>>>fromResult(null));
    // Watch this! ______,^   I'm writing C++ in Java!

    final List<String> suggestions = uid.suggest("nomatch");
    assertEquals(0, suggestions.size())// No results.

    verify(fake_scanner).setStartKey("nomatch".getBytes());
    verify(fake_scanner).setStopKey("nomatci".getBytes());
    verify(fake_scanner).setFamily(ID);
    verify(fake_scanner).setQualifier(kind_array);
  }

  @PrepareForTest({HBaseClient.class, Scanner.class})
  @Test
  public void suggestWithMatches() {
    uid = new UniqueId(client, table, kind, 3);

    final Scanner fake_scanner = mock(Scanner.class);
    when(client.newScanner(table))
      .thenReturn(fake_scanner);

    final ArrayList<ArrayList<KeyValue>> rows = new ArrayList<ArrayList<KeyValue>>(2);
    final byte[] foo_bar_id = { 0, 0, 1 };
    {
      ArrayList<KeyValue> row = new ArrayList<KeyValue>(1);
      row.add(new KeyValue("foo.bar".getBytes(), ID, kind_array, foo_bar_id));
      rows.add(row);
      row = new ArrayList<KeyValue>(1);
      row.add(new KeyValue("foo.baz".getBytes(), ID, kind_array,
                           new byte[] { 0, 0, 2 }));
      rows.add(row);
    }
    when(fake_scanner.nextRows())
      .thenReturn(Deferred.<ArrayList<ArrayList<KeyValue>>>fromResult(rows))
      .thenReturn(Deferred.<ArrayList<ArrayList<KeyValue>>>fromResult(null));
    // Watch this! ______,^   I'm writing C++ in Java!

    final List<String> suggestions = uid.suggest("foo");
    final ArrayList<String> expected = new ArrayList<String>(2);
    expected.add("foo.bar");
    expected.add("foo.baz");
    assertEquals(expected, suggestions);
    // Verify that we cached the forward + backwards mapping for both results
    // we "discovered" as a result of the scan.
    assertEquals(4, uid.cacheSize());
    assertEquals(0, uid.cacheHits());

    // Verify that the cached results are usable.
    // Should be a cache hit ...
    assertArrayEquals(foo_bar_id, uid.getOrCreateId("foo.bar"));
    assertEquals(1, uid.cacheHits());
    // ... so verify there was no HBase Get.
    verify(client, never()).get(anyGet());
  }

  @Test
  public void uidToString() {
    assertEquals("01", UniqueId.uidToString(new byte[] { 1 }));
  }
 
  @Test
  public void uidToString2() {
    assertEquals("0A0B", UniqueId.uidToString(new byte[] { 10, 11 }));
  }
 
  @Test
  public void uidToString3() {
    assertEquals("1A1B", UniqueId.uidToString(new byte[] { 26, 27 }));
  }
 
  @Test
  public void uidToStringZeros() {
    assertEquals("00", UniqueId.uidToString(new byte[] { 0 }));
  }
 
  @Test
  public void uidToString255() {
    assertEquals("FF", UniqueId.uidToString(new byte[] { (byte) 255 }));
  }
 
  @Test (expected = NullPointerException.class)
  public void uidToStringNull() {
    UniqueId.uidToString(null);
  }
 
  @Test
  public void stringToUid() {
    assertArrayEquals(new byte[] { 0x0a, 0x0b }, UniqueId.stringToUid("0A0B"));
  }
 
  @Test
  public void stringToUidNormalize() {
    assertArrayEquals(new byte[] { (byte) 171 }, UniqueId.stringToUid("AB"));
  }
 
  @Test
  public void stringToUidCase() {
    assertArrayEquals(new byte[] { (byte) 11 }, UniqueId.stringToUid("B"));
  }
 
  @Test
  public void stringToUidWidth() {
    assertArrayEquals(new byte[] { (byte) 0, (byte) 42, (byte) 12 },
        UniqueId.stringToUid("2A0C", (short)3));
  }
 
  @Test
  public void stringToUidWidth2() {
    assertArrayEquals(new byte[] { (byte) 0, (byte) 0, (byte) 0 },
        UniqueId.stringToUid("0", (short)3));
  }
 
  @Test (expected = IllegalArgumentException.class)
  public void stringToUidNull() {
    UniqueId.stringToUid(null);
  }
 
  @Test (expected = IllegalArgumentException.class)
  public void stringToUidEmpty() {
    UniqueId.stringToUid("");
  }
 
  @Test (expected = IllegalArgumentException.class)
  public void stringToUidNotHex() {
    UniqueId.stringToUid("HelloWorld");
  }
 
  @Test (expected = IllegalArgumentException.class)
  public void stringToUidNotHex2() {
    UniqueId.stringToUid(" ");
  }
 
  @Test
  public void getTSUIDFromKey() {
    final byte[] tsuid = UniqueId.getTSUIDFromKey(new byte[]
      { 0, 0, 1, 1, 1, 1, 1, 0, 0, 2, 0, 0, 3 }, (short)3, (short)4);
    assertArrayEquals(new byte[] { 0, 0, 1, 0, 0, 2, 0, 0, 3 },
        tsuid);
  }
 
  @Test
  public void getTSUIDFromKeyMissingTags() {
    final byte[] tsuid = UniqueId.getTSUIDFromKey(new byte[]
      { 0, 0, 1, 1, 1, 1, 1 }, (short)3, (short)4);
    assertArrayEquals(new byte[] { 0, 0, 1 },
        tsuid);
  }
 
  @Test
  public void getTagPairsFromTSUID() {
    List<byte[]> tags = UniqueId.getTagPairsFromTSUID(
        "000000000001000002000003000004",
        (short)3, (short)3, (short)3);
    assertNotNull(tags);
    assertEquals(4, tags.size());
    assertArrayEquals(new byte[] { 0, 0, 1 }, tags.get(0));
    assertArrayEquals(new byte[] { 0, 0, 2 }, tags.get(1));
    assertArrayEquals(new byte[] { 0, 0, 3 }, tags.get(2));
    assertArrayEquals(new byte[] { 0, 0, 4 }, tags.get(3));
  }
 
  @Test
  public void getTagPairsFromTSUIDNonStandardWidth() {
    List<byte[]> tags = UniqueId.getTagPairsFromTSUID(
        "0000000000000100000200000003000004"
        (short)3, (short)4, (short)3);
    assertNotNull(tags);
    assertEquals(4, tags.size());
    assertArrayEquals(new byte[] { 0, 0, 0, 1 }, tags.get(0));
    assertArrayEquals(new byte[] { 0, 0, 2 }, tags.get(1));
    assertArrayEquals(new byte[] { 0, 0, 0, 3 }, tags.get(2));
    assertArrayEquals(new byte[] { 0, 0, 4 }, tags.get(3));
  }
 
  @Test (expected = IllegalArgumentException.class)
  public void getTagPairsFromTSUIDMissingTags() {
    UniqueId.getTagPairsFromTSUID("123456", (short)3, (short)3, (short)3);
  }
 
  @Test (expected = IllegalArgumentException.class)
  public void getTagPairsFromTSUIDMissingMetric() {
    UniqueId.getTagPairsFromTSUID("000001000002", (short)3, (short)3, (short)3);
  }
 
  @Test (expected = IllegalArgumentException.class)
  public void getTagPairsFromTSUIDOddNumberOfCharacters() {
    UniqueId.getTagPairsFromTSUID("0000080000010000020",
        (short)3, (short)3, (short)3);
  }
 
  @Test (expected = IllegalArgumentException.class)
  public void getTagPairsFromTSUIDMissingTagv() {
    UniqueId.getTagPairsFromTSUID("000008000001",
        (short)3, (short)3, (short)3);
  }
 
  @Test (expected = IllegalArgumentException.class)
  public void getTagPairsFromTSUIDNull() {
    UniqueId.getTagPairsFromTSUID(null, (short)3, (short)3, (short)3);
  }
 
  @Test (expected = IllegalArgumentException.class)
  public void getTagPairsFromTSUIDEmpty() {
    UniqueId.getTagPairsFromTSUID("", (short)3, (short)3, (short)3);
  }
 
  @Test
  public void getUsedUIDs() throws Exception {
    final ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(3);
    final byte[] metrics = { 'm', 'e', 't', 'r', 'i', 'c', 's' };
    final byte[] tagk = { 't', 'a', 'g', 'k' };
    final byte[] tagv = { 't', 'a', 'g', 'v' };
    kvs.add(new KeyValue(MAXID, ID, metrics, Bytes.fromLong(64L)));
    kvs.add(new KeyValue(MAXID, ID, tagk, Bytes.fromLong(42L)));
    kvs.add(new KeyValue(MAXID, ID, tagv, Bytes.fromLong(1024L)));
    final TSDB tsdb = mock(TSDB.class);
    when(tsdb.getClient()).thenReturn(client);
    when(tsdb.uidTable()).thenReturn(new byte[] { 'u', 'i', 'd' });
    when(client.get(anyGet()))
      .thenReturn(Deferred.fromResult(kvs));
   
    final byte[][] kinds = { metrics, tagk, tagv };
    final Map<String, Long> uids = UniqueId.getUsedUIDs(tsdb, kinds)
      .joinUninterruptibly();
    assertNotNull(uids);
    assertEquals(3, uids.size());
    assertEquals(64L, uids.get("metrics").longValue());
    assertEquals(42L, uids.get("tagk").longValue());
    assertEquals(1024L, uids.get("tagv").longValue());
  }
 
  @Test
  public void getUsedUIDsEmptyRow() throws Exception {
    final ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(0);
    final byte[] metrics = { 'm', 'e', 't', 'r', 'i', 'c', 's' };
    final byte[] tagk = { 't', 'a', 'g', 'k' };
    final byte[] tagv = { 't', 'a', 'g', 'v' };
    final TSDB tsdb = mock(TSDB.class);
    when(tsdb.getClient()).thenReturn(client);
    when(tsdb.uidTable()).thenReturn(new byte[] { 'u', 'i', 'd' });
    when(client.get(anyGet()))
      .thenReturn(Deferred.fromResult(kvs));
   
    final byte[][] kinds = { metrics, tagk, tagv };
    final Map<String, Long> uids = UniqueId.getUsedUIDs(tsdb, kinds)
      .joinUninterruptibly();
    assertNotNull(uids);
    assertEquals(3, uids.size());
    assertEquals(0L, uids.get("metrics").longValue());
    assertEquals(0L, uids.get("tagk").longValue());
    assertEquals(0L, uids.get("tagv").longValue());
  }
 
  // ----------------- //
  // Helper functions. //
  // ----------------- //

  private static byte[] emptyArray() {
    return eq(HBaseClient.EMPTY_ARRAY);
  }

  private static GetRequest anyGet() {
    return any(GetRequest.class);
  }

  private static AtomicIncrementRequest incrementForRow(final byte[] row) {
    return argThat(new ArgumentMatcher<AtomicIncrementRequest>() {
      public boolean matches(Object incr) {
        return Arrays.equals(((AtomicIncrementRequest) incr).key(), row);
      }
      public void describeTo(org.hamcrest.Description description) {
        description.appendText("AtomicIncrementRequest for row "
                               + Arrays.toString(row));
      }
    });
  }

  private static PutRequest anyPut() {
    return any(PutRequest.class);
  }
 
  @SuppressWarnings("unchecked")
  private static Callback<byte[], ArrayList<KeyValue>> anyByteCB() {
    return any(Callback.class);
  }

  private static PutRequest putForRow(final byte[] row) {
    return argThat(new ArgumentMatcher<PutRequest>() {
      public boolean matches(Object put) {
        return Arrays.equals(((PutRequest) put).key(), row);
      }
      public void describeTo(org.hamcrest.Description description) {
        description.appendText("PutRequest for row " + Arrays.toString(row));
      }
    });
  }

  private static HBaseException fakeHBaseException() {
    final HBaseException hbe = mock(HBaseException.class);
    when(hbe.getStackTrace())
      // Truncate the stack trace because otherwise it's gigantic.
      .thenReturn(Arrays.copyOf(new RuntimeException().getStackTrace(), 3));
    when(hbe.getMessage())
      .thenReturn("fake exception");
    return hbe;
  }

  private static final byte[] MAXID = { 0 };

}
TOP

Related Classes of net.opentsdb.uid.TestUniqueId

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.