001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.util;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertNotNull;
023import static org.junit.Assert.assertTrue;
024import java.util.Random;
025import java.util.TreeMap;
026import org.apache.hadoop.hbase.HBaseClassTestRule;
027import org.apache.hadoop.hbase.testclassification.MediumTests;
028import org.apache.hadoop.hbase.testclassification.MiscTests;
029import org.apache.hadoop.hbase.util.AvlUtil.AvlIterableList;
030import org.apache.hadoop.hbase.util.AvlUtil.AvlKeyComparator;
031import org.apache.hadoop.hbase.util.AvlUtil.AvlLinkedNode;
032import org.apache.hadoop.hbase.util.AvlUtil.AvlNode;
033import org.apache.hadoop.hbase.util.AvlUtil.AvlNodeVisitor;
034import org.apache.hadoop.hbase.util.AvlUtil.AvlTree;
035import org.apache.hadoop.hbase.util.AvlUtil.AvlTreeIterator;
036import org.junit.ClassRule;
037import org.junit.Test;
038import org.junit.experimental.categories.Category;
039
040@Category({MiscTests.class, MediumTests.class})
041public class TestAvlUtil {
042
043  @ClassRule
044  public static final HBaseClassTestRule CLASS_RULE =
045      HBaseClassTestRule.forClass(TestAvlUtil.class);
046
047  private static final TestAvlKeyComparator KEY_COMPARATOR = new TestAvlKeyComparator();
048
049  @Test
050  public void testAvlTreeCrud() {
051    final int MAX_KEY = 99999999;
052    final int NELEM = 10000;
053
054    final TreeMap<Integer, Object> treeMap = new TreeMap<>();
055    TestAvlNode root = null;
056
057    final Random rand = new Random();
058    for (int i = 0; i < NELEM; ++i) {
059      int key = rand.nextInt(MAX_KEY);
060      if (AvlTree.get(root, key, KEY_COMPARATOR) != null) {
061        i--;
062        continue;
063      }
064      root = AvlTree.insert(root, new TestAvlNode(key));
065      treeMap.put(key, null);
066      for (Integer keyX: treeMap.keySet()) {
067        TestAvlNode node = AvlTree.get(root, keyX, KEY_COMPARATOR);
068        assertNotNull(node);
069        assertEquals(keyX.intValue(), node.getKey());
070      }
071    }
072
073    for (int i = 0; i < NELEM; ++i) {
074      int key = rand.nextInt(MAX_KEY);
075      TestAvlNode node = AvlTree.get(root, key, KEY_COMPARATOR);
076      if (!treeMap.containsKey(key)) {
077        assert node == null;
078        continue;
079      }
080      treeMap.remove(key);
081      assertEquals(key, node.getKey());
082      root = AvlTree.remove(root, key, KEY_COMPARATOR);
083      for (Integer keyX: treeMap.keySet()) {
084        node = AvlTree.get(root, keyX, KEY_COMPARATOR);
085        assertNotNull(node);
086        assertEquals(keyX.intValue(), node.getKey());
087      }
088    }
089  }
090
091  @Test
092  public void testAvlTreeVisitor() {
093    final int MIN_KEY = 0;
094    final int MAX_KEY = 50;
095
096    TestAvlNode root = null;
097    for (int i = MAX_KEY; i >= MIN_KEY; --i) {
098      root = AvlTree.insert(root, new TestAvlNode(i));
099    }
100
101    AvlTree.visit(root, new AvlNodeVisitor<TestAvlNode>() {
102      private int prevKey = -1;
103      @Override
104      public boolean visitNode(TestAvlNode node) {
105        assertEquals(prevKey, node.getKey() - 1);
106        assertTrue(node.getKey() >= MIN_KEY);
107        assertTrue(node.getKey() <= MAX_KEY);
108        prevKey = node.getKey();
109        return node.getKey() <= MAX_KEY;
110      }
111    });
112  }
113
114  @Test
115  public void testAvlTreeIterSeekFirst() {
116    final int MIN_KEY = 1;
117    final int MAX_KEY = 50;
118
119    TestAvlNode root = null;
120    for (int i = MIN_KEY; i < MAX_KEY; ++i) {
121      root = AvlTree.insert(root, new TestAvlNode(i));
122    }
123
124    AvlTreeIterator<TestAvlNode> iter = new AvlTreeIterator<>(root);
125    assertTrue(iter.hasNext());
126    long prevKey = 0;
127    while (iter.hasNext()) {
128      TestAvlNode node = iter.next();
129      assertEquals(prevKey + 1, node.getKey());
130      prevKey = node.getKey();
131    }
132    assertEquals(MAX_KEY - 1, prevKey);
133  }
134
135  @Test
136  public void testAvlTreeIterSeekTo() {
137    final int MIN_KEY = 1;
138    final int MAX_KEY = 50;
139
140    TestAvlNode root = null;
141    for (int i = MIN_KEY; i < MAX_KEY; i += 2) {
142      root = AvlTree.insert(root, new TestAvlNode(i));
143    }
144
145    for (int i = MIN_KEY - 1; i < MAX_KEY + 1; ++i) {
146      AvlTreeIterator<TestAvlNode> iter = new AvlTreeIterator<>(root, i, KEY_COMPARATOR);
147      if (i < MAX_KEY) {
148        assertTrue(iter.hasNext());
149      } else {
150        // searching for something greater than the last node
151        assertFalse(iter.hasNext());
152        break;
153      }
154
155      TestAvlNode node = iter.next();
156      assertEquals((i % 2 == 0) ? i + 1 : i, node.getKey());
157
158      long prevKey = node.getKey();
159      while (iter.hasNext()) {
160        node = iter.next();
161        assertTrue(node.getKey() > prevKey);
162        prevKey = node.getKey();
163      }
164    }
165  }
166
167  @Test
168  public void testAvlIterableListCrud() {
169    final int NITEMS = 10;
170    TestLinkedAvlNode prependHead = null;
171    TestLinkedAvlNode appendHead = null;
172    // prepend()/append()
173    for (int i = 0; i <= NITEMS; ++i) {
174      TestLinkedAvlNode pNode = new TestLinkedAvlNode(i);
175      assertFalse(AvlIterableList.isLinked(pNode));
176      prependHead = AvlIterableList.prepend(prependHead, pNode);
177      assertTrue(AvlIterableList.isLinked(pNode));
178
179      TestLinkedAvlNode aNode = new TestLinkedAvlNode(i);
180      assertFalse(AvlIterableList.isLinked(aNode));
181      appendHead = AvlIterableList.append(appendHead, aNode);
182      assertTrue(AvlIterableList.isLinked(aNode));
183    }
184    // readNext()
185    TestLinkedAvlNode pNode = prependHead;
186    TestLinkedAvlNode aNode = appendHead;
187    for (int i = 0; i <= NITEMS; ++i) {
188      assertEquals(NITEMS - i, pNode.getKey());
189      pNode = AvlIterableList.readNext(pNode);
190
191      assertEquals(i, aNode.getKey());
192      aNode = AvlIterableList.readNext(aNode);
193    }
194    // readPrev()
195    pNode = AvlIterableList.readPrev(prependHead);
196    aNode = AvlIterableList.readPrev(appendHead);
197    for (int i = 0; i <= NITEMS; ++i) {
198      assertEquals(i, pNode.getKey());
199      pNode = AvlIterableList.readPrev(pNode);
200
201      assertEquals(NITEMS - i, aNode.getKey());
202      aNode = AvlIterableList.readPrev(aNode);
203    }
204    // appendList()
205    TestLinkedAvlNode node = AvlIterableList.appendList(prependHead, appendHead);
206    for (int i = NITEMS; i >= 0; --i) {
207      assertEquals(i, node.getKey());
208      node = AvlIterableList.readNext(node);
209    }
210    for (int i = 0; i <= NITEMS; ++i) {
211      assertEquals(i, node.getKey());
212      node = AvlIterableList.readNext(node);
213    }
214  }
215
216  private static class TestAvlNode extends AvlNode<TestAvlNode> {
217    private final int key;
218
219    public TestAvlNode(int key) {
220      this.key = key;
221    }
222
223    public int getKey() {
224      return key;
225    }
226
227    @Override
228    public int compareTo(TestAvlNode other) {
229      return this.key - other.key;
230    }
231
232    @Override
233    public String toString() {
234      return String.format("TestAvlNode(%d)", key);
235    }
236  }
237
238  private static class TestLinkedAvlNode extends AvlLinkedNode<TestLinkedAvlNode> {
239    private final int key;
240
241    public TestLinkedAvlNode(int key) {
242      this.key = key;
243    }
244
245    public int getKey() {
246      return key;
247    }
248
249    @Override
250    public int compareTo(TestLinkedAvlNode other) {
251      return this.key - other.key;
252    }
253
254    @Override
255    public String toString() {
256      return String.format("TestLinkedAvlNode(%d)", key);
257    }
258  }
259
260  private static class TestAvlKeyComparator implements AvlKeyComparator<TestAvlNode> {
261    @Override
262    public int compareKey(TestAvlNode node, Object key) {
263      return node.getKey() - (int)key;
264    }
265  }
266}