001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: KeyRangeTest.java,v 1.38.2.3 2008/01/07 15:14:23 cwl Exp $
007: */
008:
009: package com.sleepycat.collections;
010:
011: import java.io.File;
012: import java.util.Arrays;
013: import java.util.Comparator;
014:
015: import junit.framework.Test;
016: import junit.framework.TestCase;
017: import junit.framework.TestSuite;
018:
019: import com.sleepycat.bind.ByteArrayBinding;
020: import com.sleepycat.collections.test.DbTestUtil;
021: import com.sleepycat.compat.DbCompat;
022: import com.sleepycat.je.Database;
023: import com.sleepycat.je.DatabaseConfig;
024: import com.sleepycat.je.DatabaseEntry;
025: import com.sleepycat.je.DatabaseException;
026: import com.sleepycat.je.Environment;
027: import com.sleepycat.je.EnvironmentConfig;
028: import com.sleepycat.je.OperationStatus;
029: import com.sleepycat.util.keyrange.KeyRange;
030: import com.sleepycat.util.keyrange.KeyRangeException;
031:
032: /**
033: * @author Mark Hayes
034: */
035: public class KeyRangeTest extends TestCase {
036:
037: private static boolean VERBOSE = false;
038:
039: private static final byte FF = (byte) 0xFF;
040:
041: private static final byte[][] KEYS = {
042: /* 0 */{ 1 },
043: /* 1 */{ FF },
044: /* 2 */{ FF, 0 },
045: /* 3 */{ FF, 0x7F },
046: /* 4 */{ FF, FF },
047: /* 5 */{ FF, FF, 0 },
048: /* 6 */{ FF, FF, 0x7F },
049: /* 7 */{ FF, FF, FF }, };
050: private static byte[][] EXTREME_KEY_BYTES = {
051: /* 0 */{ 0 },
052: /* 1 */{ FF, FF, FF, FF }, };
053:
054: private Environment env;
055: private Database store;
056: private DataView view;
057: private DataCursor cursor;
058:
059: public static void main(String[] args) throws Exception {
060:
061: junit.framework.TestResult tr = junit.textui.TestRunner
062: .run(suite());
063: if (tr.errorCount() > 0 || tr.failureCount() > 0) {
064: System.exit(1);
065: } else {
066: System.exit(0);
067: }
068: }
069:
070: public static Test suite() {
071:
072: return new TestSuite(KeyRangeTest.class);
073: }
074:
075: public KeyRangeTest(String name) {
076:
077: super (name);
078: }
079:
080: public void setUp() throws Exception {
081:
082: DbTestUtil.printTestName(DbTestUtil.qualifiedTestName(this ));
083: }
084:
085: private void openDb(Comparator comparator) throws Exception {
086:
087: File dir = DbTestUtil.getNewDir();
088: ByteArrayBinding dataBinding = new ByteArrayBinding();
089: EnvironmentConfig envConfig = new EnvironmentConfig();
090: envConfig.setAllowCreate(true);
091: DbCompat.setInitializeCache(envConfig, true);
092: env = new Environment(dir, envConfig);
093: DatabaseConfig dbConfig = new DatabaseConfig();
094: DbCompat.setTypeBtree(dbConfig);
095: dbConfig.setAllowCreate(true);
096: if (comparator != null) {
097: DbCompat.setBtreeComparator(dbConfig, comparator);
098: }
099: store = DbCompat.openDatabase(env, null, "test.db", null,
100: dbConfig);
101: view = new DataView(store, dataBinding, dataBinding, null,
102: true, null);
103: }
104:
105: private void closeDb() throws Exception {
106:
107: store.close();
108: store = null;
109: env.close();
110: env = null;
111: }
112:
113: public void tearDown() throws Exception {
114:
115: try {
116: if (store != null) {
117: store.close();
118: }
119: } catch (Exception e) {
120: System.out.println("Exception ignored during close: " + e);
121: }
122: try {
123: if (env != null) {
124: env.close();
125: }
126: } catch (Exception e) {
127: System.out.println("Exception ignored during close: " + e);
128: }
129: /* Ensure that GC can cleanup. */
130: env = null;
131: store = null;
132: view = null;
133: cursor = null;
134: }
135:
136: public void testScan() throws Exception {
137: openDb(null);
138: doScan(false);
139: closeDb();
140: }
141:
142: public void testScanComparator() throws Exception {
143: openDb(new ReverseComparator());
144: doScan(true);
145: closeDb();
146: }
147:
148: private void doScan(boolean reversed) throws Exception {
149:
150: byte[][] keys = new byte[KEYS.length][];
151: final int end = KEYS.length - 1;
152: cursor = new DataCursor(view, true);
153: for (int i = 0; i <= end; i++) {
154: keys[i] = KEYS[i];
155: cursor.put(keys[i], KEYS[i], null, false);
156: }
157: cursor.close();
158: byte[][] extremeKeys = new byte[EXTREME_KEY_BYTES.length][];
159: for (int i = 0; i < extremeKeys.length; i++) {
160: extremeKeys[i] = EXTREME_KEY_BYTES[i];
161: }
162:
163: // with empty range
164:
165: cursor = new DataCursor(view, false);
166: expectRange(KEYS, 0, end, reversed);
167: cursor.close();
168:
169: // begin key only, inclusive
170:
171: for (int i = 0; i <= end; i++) {
172: cursor = newCursor(view, keys[i], true, null, false,
173: reversed);
174: expectRange(KEYS, i, end, reversed);
175: cursor.close();
176: }
177:
178: // begin key only, exclusive
179:
180: for (int i = 0; i <= end; i++) {
181: cursor = newCursor(view, keys[i], false, null, false,
182: reversed);
183: expectRange(KEYS, i + 1, end, reversed);
184: cursor.close();
185: }
186:
187: // end key only, inclusive
188:
189: for (int i = 0; i <= end; i++) {
190: cursor = newCursor(view, null, false, keys[i], true,
191: reversed);
192: expectRange(KEYS, 0, i, reversed);
193: cursor.close();
194: }
195:
196: // end key only, exclusive
197:
198: for (int i = 0; i <= end; i++) {
199: cursor = newCursor(view, null, false, keys[i], false,
200: reversed);
201: expectRange(KEYS, 0, i - 1, reversed);
202: cursor.close();
203: }
204:
205: // begin and end keys, inclusive and exclusive
206:
207: for (int i = 0; i <= end; i++) {
208: for (int j = i; j <= end; j++) {
209: // begin inclusive, end inclusive
210:
211: cursor = newCursor(view, keys[i], true, keys[j], true,
212: reversed);
213: expectRange(KEYS, i, j, reversed);
214: cursor.close();
215:
216: // begin inclusive, end exclusive
217:
218: cursor = newCursor(view, keys[i], true, keys[j], false,
219: reversed);
220: expectRange(KEYS, i, j - 1, reversed);
221: cursor.close();
222:
223: // begin exclusive, end inclusive
224:
225: cursor = newCursor(view, keys[i], false, keys[j], true,
226: reversed);
227: expectRange(KEYS, i + 1, j, reversed);
228: cursor.close();
229:
230: // begin exclusive, end exclusive
231:
232: cursor = newCursor(view, keys[i], false, keys[j],
233: false, reversed);
234: expectRange(KEYS, i + 1, j - 1, reversed);
235: cursor.close();
236: }
237: }
238:
239: // single key range
240:
241: for (int i = 0; i <= end; i++) {
242: cursor = new DataCursor(view, false, keys[i]);
243: expectRange(KEYS, i, i, reversed);
244: cursor.close();
245: }
246:
247: // start with lower extreme (before any existing key)
248:
249: cursor = newCursor(view, extremeKeys[0], true, null, false,
250: reversed);
251: expectRange(KEYS, 0, end, reversed);
252: cursor.close();
253:
254: // start with higher extreme (after any existing key)
255:
256: cursor = newCursor(view, null, false, extremeKeys[1], true,
257: reversed);
258: expectRange(KEYS, 0, end, reversed);
259: cursor.close();
260: }
261:
262: private DataCursor newCursor(DataView view, Object beginKey,
263: boolean beginInclusive, Object endKey,
264: boolean endInclusive, boolean reversed) throws Exception {
265:
266: if (reversed) {
267: return new DataCursor(view, false, endKey, endInclusive,
268: beginKey, beginInclusive);
269: } else {
270: return new DataCursor(view, false, beginKey,
271: beginInclusive, endKey, endInclusive);
272: }
273: }
274:
275: private void expectRange(byte[][] bytes, int first, int last,
276: boolean reversed) throws DatabaseException {
277:
278: int i;
279: boolean init;
280: for (init = true, i = first;; i++, init = false) {
281: if (checkRange(bytes, first, last, i <= last, reversed,
282: !reversed, init, i)) {
283: break;
284: }
285: }
286: for (init = true, i = last;; i--, init = false) {
287: if (checkRange(bytes, first, last, i >= first, reversed,
288: reversed, init, i)) {
289: break;
290: }
291: }
292: }
293:
294: private boolean checkRange(byte[][] bytes, int first, int last,
295: boolean inRange, boolean reversed, boolean forward,
296: boolean init, int i) throws DatabaseException {
297:
298: OperationStatus s;
299: if (forward) {
300: if (init) {
301: s = cursor.getFirst(false);
302: } else {
303: s = cursor.getNext(false);
304: }
305: } else {
306: if (init) {
307: s = cursor.getLast(false);
308: } else {
309: s = cursor.getPrev(false);
310: }
311: }
312:
313: String msg = " " + (forward ? "next" : "prev") + " i=" + i
314: + " first=" + first + " last=" + last
315: + (reversed ? " reversed" : " not reversed");
316:
317: // check that moving past ends doesn't move the cursor
318: if (s == OperationStatus.SUCCESS && i == first) {
319: OperationStatus s2 = reversed ? cursor.getNext(false)
320: : cursor.getPrev(false);
321: assertEquals(msg, OperationStatus.NOTFOUND, s2);
322: }
323: if (s == OperationStatus.SUCCESS && i == last) {
324: OperationStatus s2 = reversed ? cursor.getPrev(false)
325: : cursor.getNext(false);
326: assertEquals(msg, OperationStatus.NOTFOUND, s2);
327: }
328:
329: byte[] val = (s == OperationStatus.SUCCESS) ? ((byte[]) cursor
330: .getCurrentValue()) : null;
331:
332: if (inRange) {
333: assertNotNull("RangeNotFound" + msg, val);
334:
335: if (!Arrays.equals(val, bytes[i])) {
336: printBytes(val);
337: printBytes(bytes[i]);
338: fail("RangeKeyNotEqual" + msg);
339: }
340: if (VERBOSE) {
341: System.out.println("GotRange" + msg);
342: }
343: return false;
344: } else {
345: assertEquals("RangeExceeded" + msg,
346: OperationStatus.NOTFOUND, s);
347: return true;
348: }
349: }
350:
351: private void printBytes(byte[] bytes) {
352:
353: for (int i = 0; i < bytes.length; i += 1) {
354: System.out.print(Integer.toHexString(bytes[i] & 0xFF));
355: System.out.print(' ');
356: }
357: System.out.println();
358: }
359:
360: public void testSubRanges() {
361:
362: DatabaseEntry begin = new DatabaseEntry();
363: DatabaseEntry begin2 = new DatabaseEntry();
364: DatabaseEntry end = new DatabaseEntry();
365: DatabaseEntry end2 = new DatabaseEntry();
366: KeyRange range = new KeyRange(null);
367: KeyRange range2;
368:
369: /* Base range [1, 2] */
370: begin.setData(new byte[] { 1 });
371: end.setData(new byte[] { 2 });
372: range = range.subRange(begin, true, end, true);
373:
374: /* Subrange (0, 1] is invalid **. */
375: begin2.setData(new byte[] { 0 });
376: end2.setData(new byte[] { 1 });
377: try {
378: range2 = range.subRange(begin2, false, end2, true);
379: fail();
380: } catch (KeyRangeException expected) {
381: }
382:
383: /* Subrange [1, 3) is invalid. */
384: begin2.setData(new byte[] { 1 });
385: end2.setData(new byte[] { 3 });
386: try {
387: range2 = range.subRange(begin2, true, end2, false);
388: fail();
389: } catch (KeyRangeException expected) {
390: }
391:
392: /* Subrange [2, 2] is valid. */
393: begin2.setData(new byte[] { 2 });
394: end2.setData(new byte[] { 2 });
395: range2 = range.subRange(begin2, true, end2, true);
396:
397: /* Subrange [0, 1] is invalid. */
398: begin2.setData(new byte[] { 0 });
399: end2.setData(new byte[] { 1 });
400: try {
401: range2 = range.subRange(begin2, true, end2, true);
402: fail();
403: } catch (KeyRangeException expected) {
404: }
405:
406: /* Subrange (0, 3] is invalid. */
407: begin2.setData(new byte[] { 0 });
408: end2.setData(new byte[] { 3 });
409: try {
410: range2 = range.subRange(begin2, false, end2, true);
411: fail();
412: } catch (KeyRangeException expected) {
413: }
414:
415: /* Subrange [3, 3) is invalid. */
416: begin2.setData(new byte[] { 3 });
417: end2.setData(new byte[] { 3 });
418: try {
419: range2 = range.subRange(begin2, true, end2, false);
420: fail();
421: } catch (KeyRangeException expected) {
422: }
423: }
424:
425: public static class ReverseComparator implements Comparator {
426: public int compare(Object o1, Object o2) {
427: byte[] d1 = (byte[]) o1;
428: byte[] d2 = (byte[]) o2;
429: int cmp = KeyRange.compareBytes(d1, 0, d1.length, d2, 0,
430: d2.length);
431: if (cmp < 0) {
432: return 1;
433: } else if (cmp > 0) {
434: return -1;
435: } else {
436: return 0;
437: }
438: }
439: }
440: }
|