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.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertFalse;
022import static org.junit.jupiter.api.Assertions.assertNotNull;
023import static org.junit.jupiter.api.Assertions.assertNull;
024import static org.junit.jupiter.api.Assertions.assertTrue;
025
026import java.util.Random;
027import java.util.TreeMap;
028import java.util.concurrent.ThreadLocalRandom;
029import org.apache.hadoop.hbase.testclassification.MediumTests;
030import org.apache.hadoop.hbase.testclassification.MiscTests;
031import org.apache.hadoop.hbase.util.AvlUtil.AvlIterableList;
032import org.apache.hadoop.hbase.util.AvlUtil.AvlKeyComparator;
033import org.apache.hadoop.hbase.util.AvlUtil.AvlLinkedNode;
034import org.apache.hadoop.hbase.util.AvlUtil.AvlNode;
035import org.apache.hadoop.hbase.util.AvlUtil.AvlNodeVisitor;
036import org.apache.hadoop.hbase.util.AvlUtil.AvlTree;
037import org.apache.hadoop.hbase.util.AvlUtil.AvlTreeIterator;
038import org.junit.jupiter.api.Tag;
039import org.junit.jupiter.api.Test;
040
041@Tag(MiscTests.TAG)
042@Tag(MediumTests.TAG)
043public class TestAvlUtil {
044
045  private static final TestAvlKeyComparator KEY_COMPARATOR = new TestAvlKeyComparator();
046
047  @Test
048  public void testAvlTreeCrud() {
049    final int MAX_KEY = 99999999;
050    final int NELEM = 10000;
051
052    final TreeMap<Integer, Object> treeMap = new TreeMap<>();
053    TestAvlNode root = null;
054
055    Random rand = ThreadLocalRandom.current();
056    for (int i = 0; i < NELEM; ++i) {
057      int key = rand.nextInt(MAX_KEY);
058      if (AvlTree.get(root, key, KEY_COMPARATOR) != null) {
059        i--;
060        continue;
061      }
062      root = AvlTree.insert(root, new TestAvlNode(key));
063      treeMap.put(key, null);
064      for (Integer keyX : treeMap.keySet()) {
065        TestAvlNode node = AvlTree.get(root, keyX, KEY_COMPARATOR);
066        assertNotNull(node);
067        assertEquals(keyX.intValue(), node.getKey());
068      }
069    }
070
071    for (int i = 0; i < NELEM; ++i) {
072      int key = rand.nextInt(MAX_KEY);
073      TestAvlNode node = AvlTree.get(root, key, KEY_COMPARATOR);
074      if (!treeMap.containsKey(key)) {
075        assertNull(node);
076        continue;
077      }
078      treeMap.remove(key);
079      assertEquals(key, node.getKey());
080      root = AvlTree.remove(root, key, KEY_COMPARATOR);
081      for (Integer keyX : treeMap.keySet()) {
082        node = AvlTree.get(root, keyX, KEY_COMPARATOR);
083        assertNotNull(node);
084        assertEquals(keyX.intValue(), node.getKey());
085      }
086    }
087  }
088
089  @Test
090  public void testAvlTreeVisitor() {
091    final int MIN_KEY = 0;
092    final int MAX_KEY = 50;
093
094    TestAvlNode root = null;
095    for (int i = MAX_KEY; i >= MIN_KEY; --i) {
096      root = AvlTree.insert(root, new TestAvlNode(i));
097    }
098
099    AvlTree.visit(root, new AvlNodeVisitor<TestAvlNode>() {
100      private int prevKey = -1;
101
102      @Override
103      public boolean visitNode(TestAvlNode node) {
104        assertEquals(prevKey, node.getKey() - 1);
105        assertTrue(node.getKey() >= MIN_KEY);
106        assertTrue(node.getKey() <= MAX_KEY);
107        prevKey = node.getKey();
108        return node.getKey() <= MAX_KEY;
109      }
110    });
111  }
112
113  @Test
114  public void testAvlTreeIterSeekFirst() {
115    final int MIN_KEY = 1;
116    final int MAX_KEY = 50;
117
118    TestAvlNode root = null;
119    for (int i = MIN_KEY; i < MAX_KEY; ++i) {
120      root = AvlTree.insert(root, new TestAvlNode(i));
121    }
122
123    AvlTreeIterator<TestAvlNode> iter = new AvlTreeIterator<>(root);
124    assertTrue(iter.hasNext());
125    long prevKey = 0;
126    while (iter.hasNext()) {
127      TestAvlNode node = iter.next();
128      assertEquals(prevKey + 1, node.getKey());
129      prevKey = node.getKey();
130    }
131    assertEquals(MAX_KEY - 1, prevKey);
132  }
133
134  @Test
135  public void testAvlTreeIterSeekTo() {
136    final int MIN_KEY = 1;
137    final int MAX_KEY = 50;
138
139    TestAvlNode root = null;
140    for (int i = MIN_KEY; i < MAX_KEY; i += 2) {
141      root = AvlTree.insert(root, new TestAvlNode(i));
142    }
143
144    for (int i = MIN_KEY - 1; i < MAX_KEY + 1; ++i) {
145      AvlTreeIterator<TestAvlNode> iter = new AvlTreeIterator<>(root, i, KEY_COMPARATOR);
146      if (i < MAX_KEY) {
147        assertTrue(iter.hasNext());
148      } else {
149        // searching for something greater than the last node
150        assertFalse(iter.hasNext());
151        break;
152      }
153
154      TestAvlNode node = iter.next();
155      assertEquals((i % 2 == 0) ? i + 1 : i, node.getKey());
156
157      long prevKey = node.getKey();
158      while (iter.hasNext()) {
159        node = iter.next();
160        assertTrue(node.getKey() > prevKey);
161        prevKey = node.getKey();
162      }
163    }
164  }
165
166  @Test
167  public void testAvlIterableListCrud() {
168    final int NITEMS = 10;
169    TestLinkedAvlNode prependHead = null;
170    TestLinkedAvlNode appendHead = null;
171    // prepend()/append()
172    for (int i = 0; i <= NITEMS; ++i) {
173      TestLinkedAvlNode pNode = new TestLinkedAvlNode(i);
174      assertFalse(AvlIterableList.isLinked(pNode));
175      prependHead = AvlIterableList.prepend(prependHead, pNode);
176      assertTrue(AvlIterableList.isLinked(pNode));
177
178      TestLinkedAvlNode aNode = new TestLinkedAvlNode(i);
179      assertFalse(AvlIterableList.isLinked(aNode));
180      appendHead = AvlIterableList.append(appendHead, aNode);
181      assertTrue(AvlIterableList.isLinked(aNode));
182    }
183    // readNext()
184    TestLinkedAvlNode pNode = prependHead;
185    TestLinkedAvlNode aNode = appendHead;
186    for (int i = 0; i <= NITEMS; ++i) {
187      assertEquals(NITEMS - i, pNode.getKey());
188      pNode = AvlIterableList.readNext(pNode);
189
190      assertEquals(i, aNode.getKey());
191      aNode = AvlIterableList.readNext(aNode);
192    }
193    // readPrev()
194    pNode = AvlIterableList.readPrev(prependHead);
195    aNode = AvlIterableList.readPrev(appendHead);
196    for (int i = 0; i <= NITEMS; ++i) {
197      assertEquals(i, pNode.getKey());
198      pNode = AvlIterableList.readPrev(pNode);
199
200      assertEquals(NITEMS - i, aNode.getKey());
201      aNode = AvlIterableList.readPrev(aNode);
202    }
203    // appendList()
204    TestLinkedAvlNode node = AvlIterableList.appendList(prependHead, appendHead);
205    for (int i = NITEMS; i >= 0; --i) {
206      assertEquals(i, node.getKey());
207      node = AvlIterableList.readNext(node);
208    }
209    for (int i = 0; i <= NITEMS; ++i) {
210      assertEquals(i, node.getKey());
211      node = AvlIterableList.readNext(node);
212    }
213  }
214
215  private static class TestAvlNode extends AvlNode<TestAvlNode> {
216    private final int key;
217
218    public TestAvlNode(int key) {
219      this.key = key;
220    }
221
222    public int getKey() {
223      return key;
224    }
225
226    @Override
227    public int compareTo(TestAvlNode other) {
228      return this.key - other.key;
229    }
230
231    @Override
232    public String toString() {
233      return String.format("TestAvlNode(%d)", key);
234    }
235  }
236
237  private static class TestLinkedAvlNode extends AvlLinkedNode<TestLinkedAvlNode> {
238    private final int key;
239
240    public TestLinkedAvlNode(int key) {
241      this.key = key;
242    }
243
244    public int getKey() {
245      return key;
246    }
247
248    @Override
249    public int compareTo(TestLinkedAvlNode other) {
250      return this.key - other.key;
251    }
252
253    @Override
254    public String toString() {
255      return String.format("TestLinkedAvlNode(%d)", key);
256    }
257  }
258
259  private static class TestAvlKeyComparator implements AvlKeyComparator<TestAvlNode> {
260    @Override
261    public int compareKey(TestAvlNode node, Object key) {
262      return node.getKey() - (int) key;
263    }
264  }
265}