// This file is part of OpenTSDB. // Copyright (C) 2013 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.tree; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.powermock.api.mockito.PowerMockito.mock; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import net.opentsdb.core.TSDB; import net.opentsdb.storage.MockBase; import net.opentsdb.utils.Config; import net.opentsdb.utils.JSON; import org.hbase.async.DeleteRequest; import org.hbase.async.GetRequest; import org.hbase.async.HBaseClient; 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 org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PowerMockIgnore({"javax.management.*", "javax.xml.*", "ch.qos.*", "org.slf4j.*", "com.sum.*", "org.xml.*"}) @PrepareForTest({ TSDB.class, HBaseClient.class, GetRequest.class, PutRequest.class, KeyValue.class, Scanner.class, DeleteRequest.class }) public final class TestBranch { private final static byte[] NAME_FAMILY = "name".getBytes(MockBase.ASCII()); private final static byte[] TREE_TABLE = "tsdb-tree".getBytes(MockBase.ASCII()); private final static byte[] UID_TABLE = "tsdb-uid".getBytes(MockBase.ASCII()); private MockBase storage; private Tree tree = TestTree.buildTestTree(); final static private Method toStorageJson; static { try { toStorageJson = Branch.class.getDeclaredMethod("toStorageJson"); toStorageJson.setAccessible(true); } catch (Exception e) { throw new RuntimeException("Failed in static initializer", e); } } final static private Method LeaftoStorageJson; static { try { LeaftoStorageJson = Leaf.class.getDeclaredMethod("toStorageJson"); LeaftoStorageJson.setAccessible(true); } catch (Exception e) { throw new RuntimeException("Failed in static initializer", e); } } @Test public void copyConstructor() { final Branch branch = buildTestBranch(tree); final Branch copy = new Branch(branch); assertEquals(1, copy.getTreeId()); assertEquals("ROOT", copy.getDisplayName()); assertNotNull(copy.getBranches()); assertTrue(copy.getBranches() != branch.getBranches()); assertNotNull(copy.getLeaves()); assertTrue(copy.getLeaves() != branch.getLeaves()); assertNotNull(copy.getPath()); assertTrue(copy.getPath() != branch.getPath()); } @Test public void testHashCode() { final Branch branch = buildTestBranch(tree); assertEquals(2521314, branch.hashCode()); } @Test public void testEquals() { final Branch branch = buildTestBranch(tree);; final Branch branch2 = buildTestBranch(tree);; assertTrue(branch.equals(branch2)); } @Test public void equalsSameAddress() { final Branch branch = buildTestBranch(tree);; assertTrue(branch.equals(branch)); } @Test public void equalsNull() { final Branch branch = buildTestBranch(tree);; assertFalse(branch.equals(null)); } @Test public void equalsWrongClass() { final Branch branch = buildTestBranch(tree);; assertFalse(branch.equals(new Object())); } @Test public void compareTo() { final Branch branch = buildTestBranch(tree);; final Branch branch2 = buildTestBranch(tree);; assertEquals(0, branch.compareTo(branch2)); } @Test public void compareToLess() { final Branch branch = buildTestBranch(tree);; final Branch branch2 = buildTestBranch(tree);; branch2.setDisplayName("Ardvark"); assertTrue(branch.compareTo(branch2) > 0); } @Test public void compareToGreater() { final Branch branch = buildTestBranch(tree);; final Branch branch2 = buildTestBranch(tree);; branch2.setDisplayName("Zelda"); assertTrue(branch.compareTo(branch2) < 0); } @Test public void getBranchIdRoot() { final Branch branch = buildTestBranch(tree);; assertEquals("0001", branch.getBranchId()); } @Test public void getBranchIdChild() { final Branch branch = buildTestBranch(tree);; assertEquals("0001D119F20E", branch.getBranches().first().getBranchId()); } @Test public void addChild() throws Exception { final Branch branch = buildTestBranch(tree); final Branch child = new Branch(tree.getTreeId()); assertTrue(branch.addChild(child)); assertEquals(3, branch.getBranches().size()); assertEquals(2, branch.getLeaves().size()); } @Test public void addChildNoLocalBranches() throws Exception { final Branch branch = buildTestBranch(tree);; final Branch child = new Branch(tree.getTreeId()); Field branches = Branch.class.getDeclaredField("branches"); branches.setAccessible(true); branches.set(branch, null); branches.setAccessible(false); assertTrue(branch.addChild(child)); assertEquals(1, branch.getBranches().size()); assertEquals(2, branch.getLeaves().size()); } @Test public void addChildNoChanges() throws Exception { final Branch branch = buildTestBranch(tree);; final Branch child = new Branch(tree.getTreeId()); assertTrue(branch.addChild(child)); assertFalse(branch.addChild(child)); assertEquals(3, branch.getBranches().size()); assertEquals(2, branch.getLeaves().size()); } @Test public void addLeafExists() throws Exception { final Tree tree = TestTree.buildTestTree(); final Branch branch = buildTestBranch(tree);; Leaf leaf = new Leaf(); leaf.setDisplayName("Alarms"); leaf.setTsuid("ABCD"); assertFalse(branch.addLeaf(leaf, tree)); assertEquals(2, branch.getBranches().size()); assertEquals(2, branch.getLeaves().size()); assertNull(tree.getCollisions()); } @Test public void addLeafCollision() throws Exception { final Tree tree = TestTree.buildTestTree(); final Branch branch = buildTestBranch(tree);; Leaf leaf = new Leaf(); leaf.setDisplayName("Alarms"); leaf.setTsuid("0001"); assertFalse(branch.addLeaf(leaf, tree)); assertEquals(2, branch.getBranches().size()); assertEquals(2, branch.getLeaves().size()); assertEquals(1, tree.getCollisions().size()); } @Test (expected = IllegalArgumentException.class) public void addChildNull() throws Exception { final Branch branch = buildTestBranch(tree);; branch.addChild(null); } @Test public void addLeaf() throws Exception { final Branch branch = buildTestBranch(tree);; Leaf leaf = new Leaf(); leaf.setDisplayName("Application Servers"); leaf.setTsuid("0004"); assertTrue(branch.addLeaf(leaf, null)); } @Test (expected = IllegalArgumentException.class) public void addLeafNull() throws Exception { final Branch branch = buildTestBranch(tree);; branch.addLeaf(null, null); } @Test public void compileBranchId() { final Branch branch = buildTestBranch(tree);; assertArrayEquals(new byte[] { 0, 1 }, branch.compileBranchId()); } @Test public void compileBranchIdChild() { final Branch branch = buildTestBranch(tree);; assertArrayEquals(new byte[] { 0, 1 , (byte) 0xD1, 0x19, (byte) 0xF2, 0x0E }, branch.getBranches().first().compileBranchId()); } @Test (expected = IllegalArgumentException.class) public void compileBranchIdEmptyDisplayName() { final Branch branch = new Branch(1); branch.compileBranchId(); } @Test (expected = IllegalArgumentException.class) public void compileBranchIdInvalidId() { final Branch branch = new Branch(0); branch.compileBranchId(); } @Test public void fetchBranch() throws Exception { setupStorage(); storage.addColumn(UID_TABLE, new byte[] { 0, 0, 1 }, NAME_FAMILY, "metrics".getBytes(MockBase.ASCII()), "sys.cpu.0".getBytes(MockBase.ASCII())); storage.addColumn(UID_TABLE, new byte[] { 0, 0, 1 }, NAME_FAMILY, "tagk".getBytes(MockBase.ASCII()), "host".getBytes(MockBase.ASCII())); storage.addColumn(UID_TABLE, new byte[] { 0, 0, 1 }, NAME_FAMILY, "tagv".getBytes(MockBase.ASCII()), "web01".getBytes(MockBase.ASCII())); storage.addColumn(UID_TABLE, new byte[] { 0, 0, 2 }, NAME_FAMILY, "metrics".getBytes(MockBase.ASCII()), "sys.cpu.1".getBytes(MockBase.ASCII())); storage.addColumn(UID_TABLE, new byte[] { 0, 0, 2 }, NAME_FAMILY, "tagk".getBytes(MockBase.ASCII()), "owner".getBytes(MockBase.ASCII())); storage.addColumn(UID_TABLE, new byte[] { 0, 0, 2 }, NAME_FAMILY, "tagv".getBytes(MockBase.ASCII()), "ops".getBytes(MockBase.ASCII())); final Branch branch = Branch.fetchBranch(storage.getTSDB(), Branch.stringToId("00010001BECD000181A8"), true).joinUninterruptibly(); assertNotNull(branch); assertEquals(1, branch.getTreeId()); assertEquals("cpu", branch.getDisplayName()); assertEquals("00010001BECD000181A8", branch.getBranchId()); assertEquals(1, branch.getBranches().size()); assertEquals(2, branch.getLeaves().size()); } @Test public void fetchBranchNSU() throws Exception { setupStorage(); storage.addColumn(UID_TABLE, new byte[] { 0, 0, 1 }, NAME_FAMILY, "metrics".getBytes(MockBase.ASCII()), "sys.cpu.0".getBytes(MockBase.ASCII())); storage.addColumn(UID_TABLE, new byte[] { 0, 0, 1 }, NAME_FAMILY, "tagk".getBytes(MockBase.ASCII()), "host".getBytes(MockBase.ASCII())); storage.addColumn(UID_TABLE, new byte[] { 0, 0, 1 }, NAME_FAMILY, "tagv".getBytes(MockBase.ASCII()), "web01".getBytes(MockBase.ASCII())); final Branch branch = Branch.fetchBranch(storage.getTSDB(), Branch.stringToId("00010001BECD000181A8"), true).joinUninterruptibly(); assertNotNull(branch); assertEquals(1, branch.getTreeId()); assertEquals("cpu", branch.getDisplayName()); assertEquals("00010001BECD000181A8", branch.getBranchId()); assertEquals(1, branch.getBranches().size()); assertEquals(1, branch.getLeaves().size()); } @Test public void fetchBranchNotFound() throws Exception { setupStorage(); final Branch branch = Branch.fetchBranch(storage.getTSDB(), Branch.stringToId("00010001BECD000181A0"), false).joinUninterruptibly(); assertNull(branch); } @Test public void fetchBranchOnly() throws Exception { setupStorage(); final Branch branch = Branch.fetchBranchOnly(storage.getTSDB(), Branch.stringToId("00010001BECD000181A8")).joinUninterruptibly(); assertNotNull(branch); assertEquals("cpu", branch.getDisplayName()); assertNull(branch.getLeaves()); assertNull(branch.getBranches()); } @Test public void fetchBranchOnlyNotFound() throws Exception { setupStorage(); final Branch branch = Branch.fetchBranchOnly(storage.getTSDB(), Branch.stringToId("00010001BECD000181A0")).joinUninterruptibly(); assertNull(branch); } @Test public void storeBranch() throws Exception { setupStorage(); final Branch branch = buildTestBranch(tree); branch.storeBranch(storage.getTSDB(), tree, true); assertEquals(3, storage.numRows(TREE_TABLE)); assertEquals(3, storage.numColumns(TREE_TABLE, new byte[] { 0, 1 })); final Branch parsed = JSON.parseToObject(storage.getColumn(TREE_TABLE, new byte[] { 0, 1 }, Tree.TREE_FAMILY(), "branch".getBytes(MockBase.ASCII())), Branch.class); parsed.setTreeId(1); assertEquals("ROOT", parsed.getDisplayName()); } @Test (expected = IllegalArgumentException.class) public void storeBranchMissingTreeID() throws Exception { setupStorage(); final Branch branch = new Branch(); branch.storeBranch(storage.getTSDB(), tree, false); } @Test (expected = IllegalArgumentException.class) public void storeBranchTreeID0() throws Exception { setupStorage(); final Branch branch = buildTestBranch(tree);; branch.setTreeId(0); branch.storeBranch(storage.getTSDB(), tree, false); } @Test (expected = IllegalArgumentException.class) public void storeBranchTreeID65536() throws Exception { setupStorage(); final Branch branch = buildTestBranch(tree);; branch.setTreeId(65536); branch.storeBranch(storage.getTSDB(), tree, false); } @Test public void storeBranchExistingLeaf() throws Exception { setupStorage(); final Branch branch = buildTestBranch(tree); Leaf leaf = new Leaf("Alarms", "ABCD"); byte[] qualifier = leaf.columnQualifier(); storage.addColumn(branch.compileBranchId(), Tree.TREE_FAMILY(), qualifier, (byte[])LeaftoStorageJson.invoke(leaf)); branch.storeBranch(storage.getTSDB(), tree, true); assertEquals(3, storage.numRows(TREE_TABLE)); assertEquals(3, storage.numColumns(TREE_TABLE, new byte[] { 0, 1 })); assertNull(tree.getCollisions()); final Branch parsed = JSON.parseToObject(storage.getColumn(TREE_TABLE, new byte[] { 0, 1 }, Tree.TREE_FAMILY(), "branch".getBytes(MockBase.ASCII())), Branch.class); parsed.setTreeId(1); assertEquals("ROOT", parsed.getDisplayName()); } @Test public void storeBranchCollision() throws Exception { setupStorage(); final Branch branch = buildTestBranch(tree); Leaf leaf = new Leaf("Alarms", "0101"); byte[] qualifier = leaf.columnQualifier(); storage.addColumn(TREE_TABLE, branch.compileBranchId(), Tree.TREE_FAMILY(), qualifier, (byte[])LeaftoStorageJson.invoke(leaf)); branch.storeBranch(storage.getTSDB(), tree, true); assertEquals(3, storage.numRows(TREE_TABLE)); assertEquals(3, storage.numColumns(TREE_TABLE, new byte[] { 0, 1 })); assertEquals(1, tree.getCollisions().size()); final Branch parsed = JSON.parseToObject(storage.getColumn(TREE_TABLE, new byte[] { 0, 1 }, Tree.TREE_FAMILY(), "branch".getBytes(MockBase.ASCII())), Branch.class); parsed.setTreeId(1); assertEquals("ROOT", parsed.getDisplayName()); } @Test public void idToString() throws Exception { assertEquals("0EA8", Branch.idToString(new byte[] { 0x0E, (byte) 0xA8 })); } @Test public void idToStringZeroes() throws Exception { assertEquals("0000", Branch.idToString(new byte[] { 0, 0 })); } @Test (expected = NullPointerException.class) public void idToStringNull() throws Exception { Branch.idToString(null); } @Test public void stringToId() throws Exception { assertArrayEquals(new byte[] { 0x0E, (byte) 0xA8 }, Branch.stringToId("0EA8")); } @Test public void stringToIdZeros() throws Exception { assertArrayEquals(new byte[] { 0, 0 }, Branch.stringToId("0000")); } @Test public void stringToIdZerosPadding() throws Exception { assertArrayEquals(new byte[] { 0, 0, 0 }, Branch.stringToId("00000")); } @Test public void stringToIdCase() throws Exception { assertArrayEquals(new byte[] { 0x0E, (byte) 0xA8 }, Branch.stringToId("0ea8")); } @Test (expected = IllegalArgumentException.class) public void stringToIdNull() throws Exception { Branch.stringToId(null); } @Test (expected = IllegalArgumentException.class) public void stringToIdEmpty() throws Exception { Branch.stringToId(""); } @Test (expected = IllegalArgumentException.class) public void stringToIdTooShort() throws Exception { Branch.stringToId("01"); } @Test (expected = IllegalArgumentException.class) public void stringToIdNotHex() throws Exception { Branch.stringToId("HelloWorld!"); } @Test public void BRANCH_QUALIFIER() throws Exception { assertArrayEquals("branch".getBytes(MockBase.ASCII()), Branch.BRANCH_QUALIFIER()); } @Test public void prependParentPath() throws Exception { Branch branch = new Branch(1); branch.setDisplayName("cpu"); final TreeMap<Integer, String> path = new TreeMap<Integer, String>(); path.put(0, "ROOT"); path.put(1, "sys"); branch.prependParentPath(path); final Map<Integer, String> compiled_path = branch.getPath(); assertNotNull(compiled_path); assertEquals(3, compiled_path.size()); } @Test public void prependParentPathEmpty() throws Exception { Branch branch = new Branch(1); branch.setDisplayName("cpu"); final TreeMap<Integer, String> path = new TreeMap<Integer, String>(); branch.prependParentPath(path); final Map<Integer, String> compiled_path = branch.getPath(); assertNotNull(compiled_path); assertEquals(1, compiled_path.size()); } @Test (expected = IllegalArgumentException.class) public void prependParentPathNull() throws Exception { new Branch().prependParentPath(null); } /** * Helper to build a default branch for testing * @return A branch with some child branches and leaves */ public static Branch buildTestBranch(final Tree tree) { final TreeMap<Integer, String> root_path = new TreeMap<Integer, String>(); final Branch root = new Branch(tree.getTreeId()); root.setDisplayName("ROOT"); root_path.put(0, "ROOT"); root.prependParentPath(root_path); Branch child = new Branch(1); child.prependParentPath(root_path); child.setDisplayName("System"); root.addChild(child); child = new Branch(tree.getTreeId()); child.prependParentPath(root_path); child.setDisplayName("Network"); root.addChild(child); Leaf leaf = new Leaf("Alarms", "ABCD"); root.addLeaf(leaf, tree); leaf = new Leaf("Employees in Office", "EF00"); root.addLeaf(leaf, tree); return root; } /** * Mocks classes for testing the storage calls */ private void setupStorage() throws Exception { final HBaseClient client = mock(HBaseClient.class); final Config config = new Config(false); storage = new MockBase(new TSDB(client, config), client, true, true, true, true); final List<byte[]> families = new ArrayList<byte[]>(); families.add(Tree.TREE_FAMILY()); storage.addTable(TREE_TABLE, families); Branch branch = new Branch(1); TreeMap<Integer, String> path = new TreeMap<Integer, String>(); path.put(0, "ROOT"); path.put(1, "sys"); path.put(2, "cpu"); branch.prependParentPath(path); branch.setDisplayName("cpu"); storage.addColumn(TREE_TABLE, branch.compileBranchId(), Tree.TREE_FAMILY(), "branch".getBytes(MockBase.ASCII()), (byte[])toStorageJson.invoke(branch)); Leaf leaf = new Leaf("user", "000001000001000001"); byte[] qualifier = leaf.columnQualifier(); storage.addColumn(TREE_TABLE, branch.compileBranchId(), Tree.TREE_FAMILY(), qualifier, (byte[])LeaftoStorageJson.invoke(leaf)); leaf = new Leaf("nice", "000002000002000002"); qualifier = leaf.columnQualifier(); storage.addColumn(TREE_TABLE, branch.compileBranchId(), Tree.TREE_FAMILY(), qualifier, (byte[])LeaftoStorageJson.invoke(leaf)); // child branch branch = new Branch(1); path.put(3, "mboard"); branch.prependParentPath(path); branch.setDisplayName("mboard"); storage.addColumn(TREE_TABLE, branch.compileBranchId(), Tree.TREE_FAMILY(), "branch".getBytes(MockBase.ASCII()), (byte[])toStorageJson.invoke(branch)); leaf = new Leaf("Asus", "000003000003000003"); qualifier = leaf.columnQualifier(); storage.addColumn(TREE_TABLE, branch.compileBranchId(), Tree.TREE_FAMILY(), qualifier, (byte[])LeaftoStorageJson.invoke(leaf)); } }