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.io.util; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.math.BigInteger; 025import java.util.Arrays; 026import org.apache.hadoop.hbase.HBaseClassTestRule; 027import org.apache.hadoop.hbase.HConstants; 028import org.apache.hadoop.hbase.testclassification.MiscTests; 029import org.apache.hadoop.hbase.testclassification.SmallTests; 030import org.apache.hadoop.hbase.util.Bytes; 031import org.junit.Before; 032import org.junit.ClassRule; 033import org.junit.Test; 034import org.junit.experimental.categories.Category; 035 036/** 037 * Tests LRUDictionary 038 */ 039@Category({ MiscTests.class, SmallTests.class }) 040public class TestLRUDictionary { 041 042 @ClassRule 043 public static final HBaseClassTestRule CLASS_RULE = 044 HBaseClassTestRule.forClass(TestLRUDictionary.class); 045 046 LRUDictionary testee; 047 048 @Before 049 public void setUp() throws Exception { 050 testee = new LRUDictionary(); 051 testee.init(Short.MAX_VALUE); 052 } 053 054 @Test 055 public void TestContainsNothing() { 056 assertTrue(isDictionaryEmpty(testee)); 057 } 058 059 /** 060 * Assert can't add empty array. 061 */ 062 @Test 063 public void testPassingEmptyArrayToFindEntry() { 064 assertEquals(Dictionary.NOT_IN_DICTIONARY, testee.findEntry(HConstants.EMPTY_BYTE_ARRAY, 0, 0)); 065 assertEquals(Dictionary.NOT_IN_DICTIONARY, testee.addEntry(HConstants.EMPTY_BYTE_ARRAY, 0, 0)); 066 } 067 068 @Test 069 public void testPassingSameArrayToAddEntry() { 070 // Add random predefined byte array, in this case a random byte array from 071 // HConstants. Assert that when we add, we get new index. Thats how it 072 // works. 073 int len = HConstants.CATALOG_FAMILY.length; 074 int index = testee.addEntry(HConstants.CATALOG_FAMILY, 0, len); 075 assertFalse(index == testee.addEntry(HConstants.CATALOG_FAMILY, 0, len)); 076 assertFalse(index == testee.addEntry(HConstants.CATALOG_FAMILY, 0, len)); 077 } 078 079 @Test 080 public void testBasic() { 081 byte[] testBytes = new byte[10]; 082 Bytes.random(testBytes); 083 084 // Verify that our randomly generated array doesn't exist in the dictionary 085 assertEquals(-1, testee.findEntry(testBytes, 0, testBytes.length)); 086 087 // now since we looked up an entry, we should have added it to the 088 // dictionary, so it isn't empty 089 090 assertFalse(isDictionaryEmpty(testee)); 091 092 // Check if we can find it using findEntry 093 short t = testee.findEntry(testBytes, 0, testBytes.length); 094 095 // Making sure we do find what we're looking for 096 assertTrue(t != -1); 097 098 byte[] testBytesCopy = new byte[20]; 099 100 Bytes.putBytes(testBytesCopy, 10, testBytes, 0, testBytes.length); 101 102 // copy byte arrays, make sure that we check that equal byte arrays are 103 // equal without just checking the reference 104 assertEquals(testee.findEntry(testBytesCopy, 10, testBytes.length), t); 105 106 // make sure the entry retrieved is the same as the one put in 107 assertTrue(Arrays.equals(testBytes, testee.getEntry(t))); 108 109 testee.clear(); 110 111 // making sure clear clears the dictionary 112 assertTrue(isDictionaryEmpty(testee)); 113 } 114 115 @Test 116 public void TestLRUPolicy() { 117 // start by filling the dictionary up with byte arrays 118 for (int i = 0; i < Short.MAX_VALUE; i++) { 119 testee.findEntry(BigInteger.valueOf(i).toByteArray(), 0, 120 BigInteger.valueOf(i).toByteArray().length); 121 } 122 123 // check we have the first element added 124 assertTrue( 125 testee.findEntry(BigInteger.ZERO.toByteArray(), 0, BigInteger.ZERO.toByteArray().length) 126 != -1); 127 128 // check for an element we know isn't there 129 assertTrue(testee.findEntry(BigInteger.valueOf(Integer.MAX_VALUE).toByteArray(), 0, 130 BigInteger.valueOf(Integer.MAX_VALUE).toByteArray().length) == -1); 131 132 // since we just checked for this element, it should be there now. 133 assertTrue(testee.findEntry(BigInteger.valueOf(Integer.MAX_VALUE).toByteArray(), 0, 134 BigInteger.valueOf(Integer.MAX_VALUE).toByteArray().length) != -1); 135 136 // test eviction, that the least recently added or looked at element is 137 // evicted. We looked at ZERO so it should be in the dictionary still. 138 assertTrue( 139 testee.findEntry(BigInteger.ZERO.toByteArray(), 0, BigInteger.ZERO.toByteArray().length) 140 != -1); 141 // Now go from beyond 1 to the end. 142 for (int i = 1; i < Short.MAX_VALUE; i++) { 143 assertTrue(testee.findEntry(BigInteger.valueOf(i).toByteArray(), 0, 144 BigInteger.valueOf(i).toByteArray().length) == -1); 145 } 146 147 // check we can find all of these. 148 for (int i = 0; i < Short.MAX_VALUE; i++) { 149 assertTrue(testee.findEntry(BigInteger.valueOf(i).toByteArray(), 0, 150 BigInteger.valueOf(i).toByteArray().length) != -1); 151 } 152 } 153 154 static private boolean isDictionaryEmpty(LRUDictionary dict) { 155 try { 156 dict.getEntry((short) 0); 157 return false; 158 } catch (IndexOutOfBoundsException ioobe) { 159 return true; 160 } 161 } 162}