1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.regionserver.wal;
20  
21  import static org.junit.Assert.*;
22  
23  import java.math.BigInteger;
24  import java.util.Arrays;
25  import java.util.Random;
26  
27  import org.apache.hadoop.hbase.HConstants;
28  import org.apache.hadoop.hbase.SmallTests;
29  import org.apache.hadoop.hbase.util.Bytes;
30  import org.junit.Before;
31  import org.junit.Test;
32  import org.junit.experimental.categories.Category;
33  
34  /**
35   * Tests LRUDictionary
36   */
37  @Category(SmallTests.class)
38  public class TestLRUDictionary {
39    LRUDictionary testee;
40  
41    @Before
42    public void setUp() throws Exception {
43      testee = new LRUDictionary();
44    }
45  
46    @Test
47    public void TestContainsNothing() {
48      assertTrue(isDictionaryEmpty(testee));
49    }
50  
51    /**
52     * Assert can't add empty array.
53     */
54    @Test
55    public void testPassingEmptyArrayToFindEntry() {
56      assertEquals(Dictionary.NOT_IN_DICTIONARY,
57        testee.findEntry(HConstants.EMPTY_BYTE_ARRAY, 0, 0));
58      assertEquals(Dictionary.NOT_IN_DICTIONARY,
59        testee.addEntry(HConstants.EMPTY_BYTE_ARRAY, 0, 0));
60    }
61  
62    @Test
63    public void testPassingSameArrayToAddEntry() {
64      // Add random predefined byte array, in this case a random byte array from
65      // HConstants.  Assert that when we add, we get new index.  Thats how it
66      // works.
67      int len = HConstants.CATALOG_FAMILY.length;
68      int index = testee.addEntry(HConstants.CATALOG_FAMILY, 0, len);
69      assertFalse(index == testee.addEntry(HConstants.CATALOG_FAMILY, 0, len));
70      assertFalse(index == testee.addEntry(HConstants.CATALOG_FAMILY, 0, len));
71    }
72  
73    @Test
74    public void testBasic() {
75      Random rand = new Random();
76      byte[] testBytes = new byte[10];
77      rand.nextBytes(testBytes);
78  
79      // Verify that our randomly generated array doesn't exist in the dictionary
80      assertEquals(testee.findEntry(testBytes, 0, testBytes.length), -1);
81  
82      // now since we looked up an entry, we should have added it to the
83      // dictionary, so it isn't empty
84  
85      assertFalse(isDictionaryEmpty(testee));
86  
87      // Check if we can find it using findEntry
88      short t = testee.findEntry(testBytes, 0, testBytes.length);
89  
90      // Making sure we do find what we're looking for
91      assertTrue(t != -1);
92  
93      byte[] testBytesCopy = new byte[20];
94  
95      Bytes.putBytes(testBytesCopy, 10, testBytes, 0, testBytes.length);
96  
97      // copy byte arrays, make sure that we check that equal byte arrays are
98      // equal without just checking the reference
99      assertEquals(testee.findEntry(testBytesCopy, 10, testBytes.length), t);
100 
101     // make sure the entry retrieved is the same as the one put in
102     assertTrue(Arrays.equals(testBytes, testee.getEntry(t)));
103 
104     testee.clear();
105 
106     // making sure clear clears the dictionary
107     assertTrue(isDictionaryEmpty(testee));
108   }
109 
110   @Test
111   public void TestLRUPolicy(){
112     //start by filling the dictionary up with byte arrays
113     for (int i = 0; i < LRUDictionary.BidirectionalLRUMap.MAX_SIZE; i++) {
114       testee.findEntry((BigInteger.valueOf(i)).toByteArray(), 0,
115           (BigInteger.valueOf(i)).toByteArray().length);
116     }
117 
118     // check we have the first element added
119     assertTrue(testee.findEntry(BigInteger.ZERO.toByteArray(), 0,
120         BigInteger.ZERO.toByteArray().length) != -1);
121 
122     // check for an element we know isn't there
123     assertTrue(testee.findEntry(BigInteger.valueOf(Integer.MAX_VALUE).toByteArray(), 0,
124         BigInteger.valueOf(Integer.MAX_VALUE).toByteArray().length) == -1);
125 
126     // since we just checked for this element, it should be there now.
127     assertTrue(testee.findEntry(BigInteger.valueOf(Integer.MAX_VALUE).toByteArray(), 0,
128         BigInteger.valueOf(Integer.MAX_VALUE).toByteArray().length) != -1);
129 
130     // test eviction, that the least recently added or looked at element is
131     // evicted.  We looked at ZERO so it should be in the dictionary still.
132     assertTrue(testee.findEntry(BigInteger.ZERO.toByteArray(), 0,
133       BigInteger.ZERO.toByteArray().length) != -1);
134     // Now go from beyond 1 to the end.
135     for(int i = 1; i < LRUDictionary.BidirectionalLRUMap.MAX_SIZE; i++) {
136       assertTrue(testee.findEntry(BigInteger.valueOf(i).toByteArray(), 0,
137           BigInteger.valueOf(i).toByteArray().length) == -1);
138     }
139 
140     // check we can find all of these.
141     for (int i = 0; i < LRUDictionary.BidirectionalLRUMap.MAX_SIZE; i++) {
142       assertTrue(testee.findEntry(BigInteger.valueOf(i).toByteArray(), 0,
143           BigInteger.valueOf(i).toByteArray().length) != -1);
144     }
145   }
146 
147   static private boolean isDictionaryEmpty(LRUDictionary dict) {
148     try {
149       dict.getEntry((short)0);
150       return false;
151     } catch (IndexOutOfBoundsException ioobe) {
152       return true;
153     }
154   }
155 }