2 *******************************************************************************
3 * Copyright (C) 1996-2010, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
8 package com.ibm.icu.dev.test.util;
10 import com.ibm.icu.dev.test.TestFmwk;
11 import com.ibm.icu.impl.CharTrie;
12 import com.ibm.icu.impl.IntTrie;
13 import com.ibm.icu.impl.IntTrieBuilder;
14 import com.ibm.icu.impl.Trie;
15 import com.ibm.icu.impl.TrieBuilder;
16 import com.ibm.icu.impl.TrieIterator;
17 import com.ibm.icu.text.UTF16;
18 import com.ibm.icu.util.RangeValueIterator;
21 * Testing class for Trie. Tests here will be simple, since both CharTrie and
22 * IntTrie are very similar and are heavily used in other parts of ICU4J.
23 * Codes using Tries are expected to have detailed tests.
24 * @author Syn Wee Quek
25 * @since release 2.1 Jan 01 2002
27 public final class TrieTest extends TestFmwk
29 // constructor ---------------------------------------------------
38 // public methods -----------------------------------------------
40 public static void main(String arg[])
42 TrieTest test = new TrieTest();
45 } catch (Exception e) {
46 test.errln("Error testing trietest");
51 * Values for setting possibly overlapping, out-of-order ranges of values
53 private static final class SetRange
55 SetRange(int start, int limit, int value, boolean overwrite)
60 this.overwrite = overwrite;
70 * value is set from the previous boundary's limit to before
71 * this boundary's limit
73 private static final class CheckRange
75 CheckRange(int limit, int value)
85 private static final class _testFoldedValue
86 implements TrieBuilder.DataManipulate
88 public _testFoldedValue(IntTrieBuilder builder)
93 public int getFoldedValue(int start, int offset)
96 int limit = start + 0x400;
97 while (start < limit) {
98 int value = m_builder_.getValue(start);
99 if (m_builder_.isInZeroBlock(start)) {
100 start += TrieBuilder.DATA_BLOCK_LENGTH;
103 foldedValue |= value;
108 if (foldedValue != 0) {
109 return (offset << 16) | foldedValue;
114 private IntTrieBuilder m_builder_;
117 private static final class _testFoldingOffset
118 implements Trie.DataManipulate
120 public int getFoldingOffset(int value)
126 private static final class _testEnumValue extends TrieIterator
128 public _testEnumValue(Trie data)
133 protected int extract(int value)
135 return value ^ 0x5555;
139 private void _testTrieIteration(IntTrie trie, CheckRange checkRanges[],
140 int countCheckRanges)
144 StringBuffer s = new StringBuffer();
145 int values[] = new int[30];
146 for (int i = 0; i < countCheckRanges; ++ i) {
147 int c = checkRanges[i].limit;
151 values[countValues ++] = checkRanges[i].value;
154 int limit = s.length();
159 int c = UTF16.charAt(s, p);
160 p += UTF16.getCharCount(c);
161 int value = trie.getCodePointValue(c);
162 if (value != values[i]) {
163 errln("wrong value from UTRIE_NEXT(U+"
164 + Integer.toHexString(c) + "): 0x"
165 + Integer.toHexString(value) + " instead of 0x"
166 + Integer.toHexString(values[i]));
168 // unlike the c version lead is 0 if c is non-supplementary
169 char lead = UTF16.getLeadSurrogate(c);
170 char trail = UTF16.getTrailSurrogate(c);
172 ? trail != s.charAt(p - 1)
173 : !UTF16.isLeadSurrogate(lead)
174 || !UTF16.isTrailSurrogate(trail) || lead != s.charAt(p - 2)
175 || trail != s.charAt(p - 1)) {
176 errln("wrong (lead, trail) from UTRIE_NEXT(U+"
177 + Integer.toHexString(c));
181 value = trie.getLeadValue(lead);
182 value = trie.getTrailValue(value, trail);
183 if (value != trie.getSurrogateValue(lead, trail)
184 && value != values[i]) {
185 errln("wrong value from getting supplementary "
187 + Integer.toHexString(c) + "): 0x"
188 + Integer.toHexString(value) + " instead of 0x"
189 + Integer.toHexString(values[i]));
196 private void _testTrieRanges(SetRange setRanges[], int countSetRanges,
197 CheckRange checkRanges[], int countCheckRanges,
198 boolean latin1Linear)
200 IntTrieBuilder newTrie = new IntTrieBuilder(null, 2000,
201 checkRanges[0].value,
202 checkRanges[0].value,
205 // set values from setRanges[]
207 for (int i = 0; i < countSetRanges; ++ i) {
208 int start = setRanges[i].start;
209 int limit = setRanges[i].limit;
210 int value = setRanges[i].value;
211 boolean overwrite = setRanges[i].overwrite;
212 if ((limit - start) == 1 && overwrite) {
213 ok &= newTrie.setValue(start, value);
216 ok &= newTrie.setRange(start, limit, value, overwrite);
220 errln("setting values into a trie failed");
224 // verify that all these values are in the new Trie
226 for (int i = 0; i < countCheckRanges; ++ i) {
227 int limit = checkRanges[i].limit;
228 int value = checkRanges[i].value;
230 while (start < limit) {
231 if (value != newTrie.getValue(start)) {
233 + Integer.toHexString(start) + "]==0x"
234 + Integer.toHexString(newTrie.getValue(start))
235 + " instead of 0x" + Integer.toHexString(value));
241 IntTrie trie = newTrie.serialize(new _testFoldedValue(newTrie),
242 new _testFoldingOffset());
244 // test linear Latin-1 range from utrie_getData()
247 for (int i = 0; i < countCheckRanges && start <= 0xff; ++ i) {
248 int limit = checkRanges[i].limit;
249 int value = checkRanges[i].value;
251 while (start < limit && start <= 0xff) {
252 if (value != trie.getLatin1LinearValue((char)start)) {
253 errln("IntTrie.getLatin1LinearValue[U+"
254 + Integer.toHexString(start) + "]==0x"
255 + Integer.toHexString(
256 trie.getLatin1LinearValue((char) start))
257 + " instead of 0x" + Integer.toHexString(value));
264 if (latin1Linear != trie.isLatin1Linear()) {
265 errln("trie serialization did not preserve "
266 + "Latin-1-linearity");
269 // verify that all these values are in the serialized Trie
271 for (int i = 0; i < countCheckRanges; ++ i) {
272 int limit = checkRanges[i].limit;
273 int value = checkRanges[i].value;
275 if (start == 0xd800) {
281 while (start < limit) {
282 if (start <= 0xffff) {
283 int value2 = trie.getBMPValue((char)start);
284 if (value != value2) {
285 errln("serialized trie.getBMPValue(U+"
286 + Integer.toHexString(start) + " == 0x"
287 + Integer.toHexString(value2) + " instead of 0x"
288 + Integer.toHexString(value));
290 if (!UTF16.isLeadSurrogate((char)start)) {
291 value2 = trie.getLeadValue((char)start);
292 if (value != value2) {
293 errln("serialized trie.getLeadValue(U+"
294 + Integer.toHexString(start) + " == 0x"
295 + Integer.toHexString(value2) + " instead of 0x"
296 + Integer.toHexString(value));
300 int value2 = trie.getCodePointValue(start);
301 if (value != value2) {
302 errln("serialized trie.getCodePointValue(U+"
303 + Integer.toHexString(start) + ")==0x"
304 + Integer.toHexString(value2) + " instead of 0x"
305 + Integer.toHexString(value));
311 // enumerate and verify all ranges
314 TrieIterator iter = new _testEnumValue(trie);
315 RangeValueIterator.Element result = new RangeValueIterator.Element();
316 while (iter.next(result)) {
317 if (result.start != checkRanges[enumRanges -1].limit
318 || result.limit != checkRanges[enumRanges].limit
319 || (result.value ^ 0x5555) != checkRanges[enumRanges].value) {
320 errln("utrie_enum() delivers wrong range [U+"
321 + Integer.toHexString(result.start) + "..U+"
322 + Integer.toHexString(result.limit) + "].0x"
323 + Integer.toHexString(result.value ^ 0x5555)
325 + Integer.toHexString(checkRanges[enumRanges -1].limit)
327 + Integer.toHexString(checkRanges[enumRanges].limit)
329 + Integer.toHexString(checkRanges[enumRanges].value));
334 // test linear Latin-1 range
335 if (trie.isLatin1Linear()) {
336 for (start = 0; start < 0x100; ++ start) {
337 if (trie.getLatin1LinearValue((char)start)
338 != trie.getLeadValue((char)start)) {
339 errln("trie.getLatin1LinearValue[U+"
340 + Integer.toHexString(start) + "]=0x"
341 + Integer.toHexString(
342 trie.getLatin1LinearValue((char)start))
344 + Integer.toHexString(
345 trie.getLeadValue((char)start)));
350 _testTrieIteration(trie, checkRanges, countCheckRanges);
353 private void _testTrieRanges2(SetRange setRanges[],
355 CheckRange checkRanges[],
356 int countCheckRanges)
358 _testTrieRanges(setRanges, countSetRanges, checkRanges, countCheckRanges,
361 _testTrieRanges(setRanges, countSetRanges, checkRanges, countCheckRanges,
365 private void _testTrieRanges4(SetRange setRanges[], int countSetRanges,
366 CheckRange checkRanges[],
367 int countCheckRanges)
369 _testTrieRanges2(setRanges, countSetRanges, checkRanges,
373 // test data ------------------------------------------------------------
376 * set consecutive ranges, even with value 0
378 private static SetRange setRanges1[]={
379 new SetRange(0, 0x20, 0, false),
380 new SetRange(0x20, 0xa7, 0x1234, false),
381 new SetRange(0xa7, 0x3400, 0, false),
382 new SetRange(0x3400, 0x9fa6, 0x6162, false),
383 new SetRange(0x9fa6, 0xda9e, 0x3132, false),
384 // try to disrupt _testFoldingOffset16()
385 new SetRange(0xdada, 0xeeee, 0x87ff, false),
386 new SetRange(0xeeee, 0x11111, 1, false),
387 new SetRange(0x11111, 0x44444, 0x6162, false),
388 new SetRange(0x44444, 0x60003, 0, false),
389 new SetRange(0xf0003, 0xf0004, 0xf, false),
390 new SetRange(0xf0004, 0xf0006, 0x10, false),
391 new SetRange(0xf0006, 0xf0007, 0x11, false),
392 new SetRange(0xf0007, 0xf0020, 0x12, false),
393 new SetRange(0xf0020, 0x110000, 0, false)
396 private static CheckRange checkRanges1[]={
397 new CheckRange(0, 0), // dummy start range to make _testEnumRange() simpler
398 new CheckRange(0x20, 0),
399 new CheckRange(0xa7, 0x1234),
400 new CheckRange(0x3400, 0),
401 new CheckRange(0x9fa6, 0x6162),
402 new CheckRange(0xda9e, 0x3132),
403 new CheckRange(0xdada, 0),
404 new CheckRange(0xeeee, 0x87ff),
405 new CheckRange(0x11111,1),
406 new CheckRange(0x44444,0x6162),
407 new CheckRange(0xf0003,0),
408 new CheckRange(0xf0004,0xf),
409 new CheckRange(0xf0006,0x10),
410 new CheckRange(0xf0007,0x11),
411 new CheckRange(0xf0020,0x12),
412 new CheckRange(0x110000, 0)
416 * set some interesting overlapping ranges
418 private static SetRange setRanges2[]={
419 new SetRange(0x21, 0x7f, 0x5555, true),
420 new SetRange(0x2f800,0x2fedc, 0x7a, true),
421 new SetRange(0x72, 0xdd, 3, true),
422 new SetRange(0xdd, 0xde, 4, false),
423 new SetRange(0x2f987,0x2fa98, 5, true),
424 new SetRange(0x2f777,0x2f833, 0, true),
425 new SetRange(0x2f900,0x2ffee, 1, false),
426 new SetRange(0x2ffee,0x2ffef, 2, true)
429 private static CheckRange checkRanges2[]={
430 // dummy start range to make _testEnumRange() simpler
431 new CheckRange(0, 0),
432 new CheckRange(0x21, 0),
433 new CheckRange(0x72, 0x5555),
434 new CheckRange(0xdd, 3),
435 new CheckRange(0xde, 4),
436 new CheckRange(0x2f833,0),
437 new CheckRange(0x2f987,0x7a),
438 new CheckRange(0x2fa98,5),
439 new CheckRange(0x2fedc,0x7a),
440 new CheckRange(0x2ffee,1),
441 new CheckRange(0x2ffef,2),
442 new CheckRange(0x110000, 0)
446 * use a non-zero initial value
448 private static SetRange setRanges3[]={
449 new SetRange(0x31, 0xa4, 1, false),
450 new SetRange(0x3400, 0x6789, 2, false),
451 new SetRange(0x30000,0x34567,9, true),
452 new SetRange(0x45678,0x56789,3, true)
455 private static CheckRange checkRanges3[]={
456 // dummy start range, also carries the initial value
457 new CheckRange(0, 9),
458 new CheckRange(0x31, 9),
459 new CheckRange(0xa4, 1),
460 new CheckRange(0x3400, 9),
461 new CheckRange(0x6789, 2),
462 new CheckRange(0x45678,9),
463 new CheckRange(0x56789,3),
464 new CheckRange(0x110000,9)
467 public void TestIntTrie()
469 _testTrieRanges4(setRanges1, setRanges1.length, checkRanges1,
470 checkRanges1.length);
471 _testTrieRanges4(setRanges2, setRanges2.length, checkRanges2,
472 checkRanges2.length);
473 _testTrieRanges4(setRanges3, setRanges3.length, checkRanges3,
474 checkRanges3.length);
477 private static class DummyGetFoldingOffset implements Trie.DataManipulate {
478 public int getFoldingOffset(int value) {
479 return -1; /* never get non-initialValue data for supplementary code points */
483 public void TestDummyCharTrie() {
485 final int initialValue=0x313, leadUnitValue=0xaffe;
488 trie=new CharTrie(initialValue, leadUnitValue, new DummyGetFoldingOffset());
490 /* test that all code points have initialValue */
491 for(c=0; c<=0x10ffff; ++c) {
492 value=trie.getCodePointValue(c);
493 if(value!=initialValue) {
494 errln("CharTrie/dummy.getCodePointValue(c)(U+"+hex(c)+")=0x"+hex(value)+" instead of 0x"+hex(initialValue));
498 /* test that the lead surrogate code units have leadUnitValue */
499 for(c=0xd800; c<=0xdbff; ++c) {
500 value=trie.getLeadValue((char)c);
501 if(value!=leadUnitValue) {
502 errln("CharTrie/dummy.getLeadValue(c)(U+"+hex(c)+")=0x"+hex(value)+" instead of 0x"+hex(leadUnitValue));
507 public void TestDummyIntTrie() {
509 final int initialValue=0x01234567, leadUnitValue=0x89abcdef;
512 trie=new IntTrie(initialValue, leadUnitValue, new DummyGetFoldingOffset());
514 /* test that all code points have initialValue */
515 for(c=0; c<=0x10ffff; ++c) {
516 value=trie.getCodePointValue(c);
517 if(value!=initialValue) {
518 errln("IntTrie/dummy.getCodePointValue(c)(U+"+hex(c)+")=0x"+hex(value)+" instead of 0x"+hex(initialValue));
522 /* test that the lead surrogate code units have leadUnitValue */
523 for(c=0xd800; c<=0xdbff; ++c) {
524 value=trie.getLeadValue((char)c);
525 if(value!=leadUnitValue) {
526 errln("IntTrie/dummy.getLeadValue(c)(U+"+hex(c)+")=0x"+hex(value)+" instead of 0x"+hex(leadUnitValue));