View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.commons.lang;
18  
19  import static org.junit.jupiter.api.Assertions.*;
20  
21  import org.apache.juneau.*;
22  import org.junit.jupiter.api.*;
23  
24  /**
25   * Tests for {@link AsciiSet}.
26   */
27  class AsciiSet_Test extends TestBase {
28  
29  	//====================================================================================================
30  	// of(String) - Static factory
31  	//====================================================================================================
32  	@Test
33  	void a01_of_basic() {
34  		var set = AsciiSet.of("abc");
35  		assertTrue(set.contains('a'));
36  		assertTrue(set.contains('b'));
37  		assertTrue(set.contains('c'));
38  		assertFalse(set.contains('d'));
39  	}
40  
41  	@Test
42  	void a02_of_empty() {
43  		var set = AsciiSet.of("");
44  		assertFalse(set.contains('a'));
45  	}
46  
47  	@Test
48  	void a03_of_withNonAscii() {
49  		var set = AsciiSet.of("abc\u1234");
50  		assertTrue(set.contains('a'));
51  		assertTrue(set.contains('b'));
52  		assertTrue(set.contains('c'));
53  		assertFalse(set.contains('\u1234')); // Non-ASCII ignored
54  	}
55  
56  	@Test
57  	void a04_of_duplicates() {
58  		var set = AsciiSet.of("aabbcc");
59  		assertTrue(set.contains('a'));
60  		assertTrue(set.contains('b'));
61  		assertTrue(set.contains('c'));
62  	}
63  
64  	//====================================================================================================
65  	// create() - Builder factory
66  	//====================================================================================================
67  	@Test
68  	void a05_create_basic() {
69  		var builder = AsciiSet.create();
70  		assertNotNull(builder);
71  		var set = builder.build();
72  		assertNotNull(set);
73  		assertFalse(set.contains('a'));
74  	}
75  
76  	//====================================================================================================
77  	// Builder.chars(char...) - Varargs
78  	//====================================================================================================
79  	@Test
80  	void b01_builderChars_varargs_basic() {
81  		var set = AsciiSet.create()
82  			.chars('a', 'b', 'c')
83  			.build();
84  		assertTrue(set.contains('a'));
85  		assertTrue(set.contains('b'));
86  		assertTrue(set.contains('c'));
87  		assertFalse(set.contains('d'));
88  	}
89  
90  	@Test
91  	void b02_builderChars_varargs_empty() {
92  		var set = AsciiSet.create()
93  			.chars()
94  			.build();
95  		assertFalse(set.contains('a'));
96  	}
97  
98  	@Test
99  	void b03_builderChars_varargs_single() {
100 		var set = AsciiSet.create()
101 			.chars('a')
102 			.build();
103 		assertTrue(set.contains('a'));
104 		assertFalse(set.contains('b'));
105 	}
106 
107 	@Test
108 	void b04_builderChars_varargs_withNonAscii() {
109 		var set = AsciiSet.create()
110 			.chars('a', 'b', '\u1234', 'c')
111 			.build();
112 		assertTrue(set.contains('a'));
113 		assertTrue(set.contains('b'));
114 		assertTrue(set.contains('c'));
115 		assertFalse(set.contains('\u1234')); // Non-ASCII ignored
116 	}
117 
118 	@Test
119 	void b05_builderChars_varargs_chaining() {
120 		var set = AsciiSet.create()
121 			.chars('a', 'b')
122 			.chars('c', 'd')
123 			.build();
124 		assertTrue(set.contains('a'));
125 		assertTrue(set.contains('b'));
126 		assertTrue(set.contains('c'));
127 		assertTrue(set.contains('d'));
128 	}
129 
130 	@Test
131 	void b06_builderChars_varargs_boundary() {
132 		var set = AsciiSet.create()
133 			.chars((char)0, (char)127)
134 			.build();
135 		assertTrue(set.contains((char)0));
136 		assertTrue(set.contains((char)127));
137 	}
138 
139 	@Test
140 	void b07_builderChars_varargs_above127() {
141 		var set = AsciiSet.create()
142 			.chars((char)128, (char)255)
143 			.build();
144 		assertFalse(set.contains((char)128));
145 		assertFalse(set.contains((char)255));
146 	}
147 
148 	//====================================================================================================
149 	// Builder.chars(String) - String
150 	//====================================================================================================
151 	@Test
152 	void c01_builderChars_string_basic() {
153 		var set = AsciiSet.create()
154 			.chars("abc")
155 			.build();
156 		assertTrue(set.contains('a'));
157 		assertTrue(set.contains('b'));
158 		assertTrue(set.contains('c'));
159 	}
160 
161 	@Test
162 	void c02_builderChars_string_empty() {
163 		var set = AsciiSet.create()
164 			.chars("")
165 			.build();
166 		assertFalse(set.contains('a'));
167 	}
168 
169 	@Test
170 	void c03_builderChars_string_withNonAscii() {
171 		var set = AsciiSet.create()
172 			.chars("abc\u1234")
173 			.build();
174 		assertTrue(set.contains('a'));
175 		assertTrue(set.contains('b'));
176 		assertTrue(set.contains('c'));
177 		assertFalse(set.contains('\u1234')); // Non-ASCII ignored
178 	}
179 
180 	@Test
181 	void c04_builderChars_string_duplicates() {
182 		var set = AsciiSet.create()
183 			.chars("aabbcc")
184 			.build();
185 		assertTrue(set.contains('a'));
186 		assertTrue(set.contains('b'));
187 		assertTrue(set.contains('c'));
188 	}
189 
190 	@Test
191 	void c05_builderChars_string_chaining() {
192 		var set = AsciiSet.create()
193 			.chars("ab")
194 			.chars("cd")
195 			.build();
196 		assertTrue(set.contains('a'));
197 		assertTrue(set.contains('b'));
198 		assertTrue(set.contains('c'));
199 		assertTrue(set.contains('d'));
200 	}
201 
202 	@Test
203 	void c06_builderChars_string_boundary() {
204 		var set = AsciiSet.create()
205 			.chars(String.valueOf((char)0) + (char)127)
206 			.build();
207 		assertTrue(set.contains((char)0));
208 		assertTrue(set.contains((char)127));
209 	}
210 
211 	//====================================================================================================
212 	// Builder.range(char, char) - Range
213 	//====================================================================================================
214 	@Test
215 	void d01_builderRange_basic() {
216 		var set = AsciiSet.create()
217 			.range('a', 'c')
218 			.build();
219 		assertTrue(set.contains('a'));
220 		assertTrue(set.contains('b'));
221 		assertTrue(set.contains('c'));
222 		assertFalse(set.contains('d'));
223 	}
224 
225 	@Test
226 	void d02_builderRange_single() {
227 		var set = AsciiSet.create()
228 			.range('a', 'a')
229 			.build();
230 		assertTrue(set.contains('a'));
231 		assertFalse(set.contains('b'));
232 	}
233 
234 	@Test
235 	void d03_builderRange_reverse() {
236 		var set = AsciiSet.create()
237 			.range('c', 'a')
238 			.build();
239 		// When start > end, the loop doesn't execute
240 		assertFalse(set.contains('a'));
241 		assertFalse(set.contains('b'));
242 		assertFalse(set.contains('c'));
243 	}
244 
245 	@Test
246 	void d04_builderRange_boundary() {
247 		var set = AsciiSet.create()
248 			.range((char)0, (char)127)
249 			.build();
250 		assertTrue(set.contains((char)0));
251 		assertTrue(set.contains((char)127));
252 		assertTrue(set.contains((char)64)); // Middle
253 	}
254 
255 	@Test
256 	void d05_builderRange_crossingAsciiBoundary() {
257 		var set = AsciiSet.create()
258 			.range('a', (char)200)
259 			.build();
260 		assertTrue(set.contains('a'));
261 		assertTrue(set.contains((char)127));
262 		// Characters > 127 are ignored
263 		assertFalse(set.contains((char)128));
264 		assertFalse(set.contains((char)200));
265 	}
266 
267 	@Test
268 	void d06_builderRange_chaining() {
269 		var set = AsciiSet.create()
270 			.range('a', 'c')
271 			.range('x', 'z')
272 			.build();
273 		assertTrue(set.contains('a'));
274 		assertTrue(set.contains('b'));
275 		assertTrue(set.contains('c'));
276 		assertTrue(set.contains('x'));
277 		assertTrue(set.contains('y'));
278 		assertTrue(set.contains('z'));
279 		assertFalse(set.contains('d'));
280 	}
281 
282 	@Test
283 	void d07_builderRange_overlapping() {
284 		var set = AsciiSet.create()
285 			.range('a', 'd')
286 			.range('c', 'f')
287 			.build();
288 		assertTrue(set.contains('a'));
289 		assertTrue(set.contains('b'));
290 		assertTrue(set.contains('c'));
291 		assertTrue(set.contains('d'));
292 		assertTrue(set.contains('e'));
293 		assertTrue(set.contains('f'));
294 	}
295 
296 	//====================================================================================================
297 	// Builder.ranges(String...) - Multiple ranges
298 	//====================================================================================================
299 	@Test
300 	void e01_builderRanges_basic() {
301 		var set = AsciiSet.create()
302 			.ranges("a-c", "x-z")
303 			.build();
304 		assertTrue(set.contains('a'));
305 		assertTrue(set.contains('b'));
306 		assertTrue(set.contains('c'));
307 		assertTrue(set.contains('x'));
308 		assertTrue(set.contains('y'));
309 		assertTrue(set.contains('z'));
310 		assertFalse(set.contains('d'));
311 	}
312 
313 	@Test
314 	void e02_builderRanges_single() {
315 		var set = AsciiSet.create()
316 			.ranges("a-c")
317 			.build();
318 		assertTrue(set.contains('a'));
319 		assertTrue(set.contains('b'));
320 		assertTrue(set.contains('c'));
321 	}
322 
323 	@Test
324 	void e03_builderRanges_empty() {
325 		var set = AsciiSet.create()
326 			.ranges()
327 			.build();
328 		assertFalse(set.contains('a'));
329 	}
330 
331 	@Test
332 	void e04_builderRanges_singleChar() {
333 		var set = AsciiSet.create()
334 			.ranges("a-a")
335 			.build();
336 		assertTrue(set.contains('a'));
337 		assertFalse(set.contains('b'));
338 	}
339 
340 	@Test
341 	void e05_builderRanges_chaining() {
342 		var set = AsciiSet.create()
343 			.ranges("a-c")
344 			.ranges("x-z")
345 			.build();
346 		assertTrue(set.contains('a'));
347 		assertTrue(set.contains('b'));
348 		assertTrue(set.contains('c'));
349 		assertTrue(set.contains('x'));
350 		assertTrue(set.contains('y'));
351 		assertTrue(set.contains('z'));
352 	}
353 
354 	@Test
355 	void e06_builderRanges_invalidLength() {
356 		assertThrows(IllegalArgumentException.class, () -> {
357 			AsciiSet.create().ranges("ab"); // Too short
358 		});
359 		assertThrows(IllegalArgumentException.class, () -> {
360 			AsciiSet.create().ranges("abcd"); // Too long
361 		});
362 	}
363 
364 	@Test
365 	void e07_builderRanges_invalidFormat() {
366 		assertThrows(IllegalArgumentException.class, () -> {
367 			AsciiSet.create().ranges("a_b"); // Wrong separator
368 		});
369 		assertThrows(IllegalArgumentException.class, () -> {
370 			AsciiSet.create().ranges("a b"); // Space instead of dash
371 		});
372 	}
373 
374 	@Test
375 	void e08_builderRanges_validFormat() {
376 		var set = AsciiSet.create()
377 			.ranges("a-z", "0-9", "A-Z")
378 			.build();
379 		assertTrue(set.contains('a'));
380 		assertTrue(set.contains('z'));
381 		assertTrue(set.contains('0'));
382 		assertTrue(set.contains('9'));
383 		assertTrue(set.contains('A'));
384 		assertTrue(set.contains('Z'));
385 	}
386 
387 	//====================================================================================================
388 	// Builder.build() - Build
389 	//====================================================================================================
390 	@Test
391 	void f01_build_empty() {
392 		var set = AsciiSet.create().build();
393 		assertNotNull(set);
394 		assertFalse(set.contains('a'));
395 	}
396 
397 	@Test
398 	void f02_build_immutable() {
399 		var builder = AsciiSet.create();
400 		builder.chars('a');
401 		var set1 = builder.build();
402 		builder.chars('b');
403 		var set2 = builder.build();
404 		// set1 should not be affected by builder changes
405 		assertTrue(set1.contains('a'));
406 		assertFalse(set1.contains('b'));
407 		// set2 should have both
408 		assertTrue(set2.contains('a'));
409 		assertTrue(set2.contains('b'));
410 	}
411 
412 	//====================================================================================================
413 	// contains(char) - Character
414 	//====================================================================================================
415 	@Test
416 	void g01_contains_char_present() {
417 		var set = AsciiSet.of("abc");
418 		assertTrue(set.contains('a'));
419 		assertTrue(set.contains('b'));
420 		assertTrue(set.contains('c'));
421 	}
422 
423 	@Test
424 	void g02_contains_char_notPresent() {
425 		var set = AsciiSet.of("abc");
426 		assertFalse(set.contains('d'));
427 		assertFalse(set.contains('x'));
428 	}
429 
430 	@Test
431 	void g03_contains_char_boundary() {
432 		var set = AsciiSet.create()
433 			.chars((char)0, (char)127)
434 			.build();
435 		assertTrue(set.contains((char)0));
436 		assertTrue(set.contains((char)127));
437 	}
438 
439 	@Test
440 	void g04_contains_char_nonAscii() {
441 		var set = AsciiSet.of("abc");
442 		assertFalse(set.contains((char)128));
443 		assertFalse(set.contains('€'));
444 	}
445 
446 	@Test
447 	void g05_contains_char_negative() {
448 		var set = AsciiSet.of("abc");
449 		// Negative char values are not valid, but let's test the behavior
450 		// In Java, char is unsigned, so this would be a large positive value
451 		assertFalse(set.contains((char)-1));
452 	}
453 
454 	//====================================================================================================
455 	// contains(int) - Integer
456 	//====================================================================================================
457 	@Test
458 	void h01_contains_int_present() {
459 		var set = AsciiSet.of("abc");
460 		assertTrue(set.contains((int)'a'));
461 		assertTrue(set.contains((int)'b'));
462 		assertTrue(set.contains((int)'c'));
463 	}
464 
465 	@Test
466 	void h02_contains_int_notPresent() {
467 		var set = AsciiSet.of("abc");
468 		assertFalse(set.contains((int)'d'));
469 	}
470 
471 	@Test
472 	void h03_contains_int_boundary() {
473 		var set = AsciiSet.create()
474 			.chars((char)0, (char)127)
475 			.build();
476 		assertTrue(set.contains(0));
477 		assertTrue(set.contains(127));
478 	}
479 
480 	@Test
481 	void h04_contains_int_negative() {
482 		var set = AsciiSet.of("abc");
483 		assertFalse(set.contains(-1));
484 	}
485 
486 	@Test
487 	void h05_contains_int_above127() {
488 		var set = AsciiSet.of("abc");
489 		assertFalse(set.contains(128));
490 		assertFalse(set.contains(1000));
491 	}
492 
493 	//====================================================================================================
494 	// contains(CharSequence) - CharSequence
495 	//====================================================================================================
496 	@Test
497 	void i01_contains_CharSequence_present() {
498 		var set = AsciiSet.of("abc");
499 		assertTrue(set.contains("abc"));
500 		assertTrue(set.contains("xyzabc"));
501 	}
502 
503 	@Test
504 	void i02_contains_CharSequence_notPresent() {
505 		var set = AsciiSet.of("abc");
506 		assertFalse(set.contains("xyz"));
507 		assertFalse(set.contains("def"));
508 	}
509 
510 	@Test
511 	void i03_contains_CharSequence_null() {
512 		var set = AsciiSet.of("abc");
513 		assertFalse(set.contains((CharSequence)null));
514 	}
515 
516 	@Test
517 	void i04_contains_CharSequence_empty() {
518 		var set = AsciiSet.of("abc");
519 		assertFalse(set.contains(""));
520 	}
521 
522 	@Test
523 	void i05_contains_CharSequence_firstChar() {
524 		var set = AsciiSet.of("abc");
525 		assertTrue(set.contains("abc"));
526 	}
527 
528 	@Test
529 	void i06_contains_CharSequence_middleChar() {
530 		var set = AsciiSet.of("b");
531 		assertTrue(set.contains("abc"));
532 	}
533 
534 	@Test
535 	void i07_contains_CharSequence_lastChar() {
536 		var set = AsciiSet.of("c");
537 		assertTrue(set.contains("abc"));
538 	}
539 
540 	@Test
541 	void i08_contains_CharSequence_singleChar() {
542 		var set = AsciiSet.of("a");
543 		assertTrue(set.contains("a"));
544 	}
545 
546 	@Test
547 	void i09_contains_CharSequence_StringBuilder() {
548 		var set = AsciiSet.of("abc");
549 		var sb = new StringBuilder("abc");
550 		assertTrue(set.contains(sb));
551 	}
552 
553 	@Test
554 	void i10_contains_CharSequence_StringBuffer() {
555 		var set = AsciiSet.of("abc");
556 		var sb = new StringBuffer("abc");
557 		assertTrue(set.contains(sb));
558 	}
559 
560 	@Test
561 	void i11_contains_CharSequence_withNonAscii() {
562 		var set = AsciiSet.of("abc");
563 		// String contains non-ASCII character, but also contains 'a'
564 		assertTrue(set.contains("a€"));
565 	}
566 
567 	@Test
568 	void i12_contains_CharSequence_onlyNonAscii() {
569 		var set = AsciiSet.of("abc");
570 		assertFalse(set.contains("€"));
571 	}
572 
573 	//====================================================================================================
574 	// containsOnly(String) - Contains only
575 	//====================================================================================================
576 	@Test
577 	void j01_containsOnly_allPresent() {
578 		var set = AsciiSet.of("abc");
579 		assertTrue(set.containsOnly("abc"));
580 		assertTrue(set.containsOnly("aabbcc"));
581 	}
582 
583 	@Test
584 	void j02_containsOnly_someNotPresent() {
585 		var set = AsciiSet.of("abc");
586 		assertFalse(set.containsOnly("abcd"));
587 		assertFalse(set.containsOnly("xyz"));
588 	}
589 
590 	@Test
591 	void j03_containsOnly_null() {
592 		var set = AsciiSet.of("abc");
593 		assertFalse(set.containsOnly(null));
594 	}
595 
596 	@Test
597 	void j04_containsOnly_empty() {
598 		var set = AsciiSet.of("abc");
599 		// Empty string should return true (all characters in empty string are in the set)
600 		assertTrue(set.containsOnly(""));
601 	}
602 
603 	@Test
604 	void j05_containsOnly_singleChar() {
605 		var set = AsciiSet.of("abc");
606 		assertTrue(set.containsOnly("a"));
607 		assertFalse(set.containsOnly("d"));
608 	}
609 
610 	@Test
611 	void j06_containsOnly_withNonAscii() {
612 		var set = AsciiSet.of("abc");
613 		// Non-ASCII characters are not in the set
614 		assertFalse(set.containsOnly("abc€"));
615 	}
616 
617 	@Test
618 	void j07_containsOnly_onlyNonAscii() {
619 		var set = AsciiSet.of("abc");
620 		// Empty string after filtering non-ASCII, but original is not empty
621 		// Actually, containsOnly checks each char, so non-ASCII will fail
622 		assertFalse(set.containsOnly("€"));
623 	}
624 
625 	@Test
626 	void j08_containsOnly_range() {
627 		var set = AsciiSet.create()
628 			.range('a', 'z')
629 			.build();
630 		assertTrue(set.containsOnly("abcdefghijklmnopqrstuvwxyz"));
631 		assertFalse(set.containsOnly("abc123"));
632 	}
633 
634 	//====================================================================================================
635 	// copy() - Copy
636 	//====================================================================================================
637 	@Test
638 	void k01_copy_basic() {
639 		var original = AsciiSet.of("abc");
640 		var builder = original.copy();
641 		var copy = builder.build();
642 		assertTrue(copy.contains('a'));
643 		assertTrue(copy.contains('b'));
644 		assertTrue(copy.contains('c'));
645 	}
646 
647 	@Test
648 	void k02_copy_modifyBuilder() {
649 		var original = AsciiSet.of("abc");
650 		var builder = original.copy();
651 		builder.chars('d');
652 		var modified = builder.build();
653 		assertTrue(modified.contains('a'));
654 		assertTrue(modified.contains('b'));
655 		assertTrue(modified.contains('c'));
656 		assertTrue(modified.contains('d'));
657 		// Original should be unchanged
658 		assertTrue(original.contains('a'));
659 		assertFalse(original.contains('d'));
660 	}
661 
662 	@Test
663 	void k03_copy_independent() {
664 		var original = AsciiSet.of("abc");
665 		var copy1 = original.copy().build();
666 		var copy2 = original.copy().chars('d').build();
667 		assertTrue(copy1.contains('a'));
668 		assertFalse(copy1.contains('d'));
669 		assertTrue(copy2.contains('a'));
670 		assertTrue(copy2.contains('d'));
671 	}
672 
673 	@Test
674 	void k04_copy_empty() {
675 		var original = AsciiSet.create().build();
676 		var copy = original.copy().build();
677 		assertFalse(copy.contains('a'));
678 	}
679 
680 	@Test
681 	void k05_copy_fullRange() {
682 		var original = AsciiSet.create()
683 			.range('a', 'z')
684 			.build();
685 		var copy = original.copy().build();
686 		for (char c = 'a'; c <= 'z'; c++) {
687 			assertTrue(copy.contains(c));
688 		}
689 	}
690 
691 	//====================================================================================================
692 	// Integration tests
693 	//====================================================================================================
694 	@Test
695 	void l01_integration_complexBuilder() {
696 		var set = AsciiSet.create()
697 			.chars('a', 'b', 'c')
698 			.chars("def")
699 			.range('g', 'i')
700 			.ranges("j-l", "m-o")
701 			.build();
702 		assertTrue(set.contains('a'));
703 		assertTrue(set.contains('b'));
704 		assertTrue(set.contains('c'));
705 		assertTrue(set.contains('d'));
706 		assertTrue(set.contains('e'));
707 		assertTrue(set.contains('f'));
708 		assertTrue(set.contains('g'));
709 		assertTrue(set.contains('h'));
710 		assertTrue(set.contains('i'));
711 		assertTrue(set.contains('j'));
712 		assertTrue(set.contains('k'));
713 		assertTrue(set.contains('l'));
714 		assertTrue(set.contains('m'));
715 		assertTrue(set.contains('n'));
716 		assertTrue(set.contains('o'));
717 		assertFalse(set.contains('p'));
718 	}
719 
720 	@Test
721 	void l02_integration_containsAndContainsOnly() {
722 		var set = AsciiSet.of("abc");
723 		assertTrue(set.contains("abc"));
724 		assertTrue(set.containsOnly("abc"));
725 		assertTrue(set.contains("xyzabc"));
726 		assertFalse(set.containsOnly("xyzabc"));
727 	}
728 
729 	@Test
730 	void l03_integration_copyAndModify() {
731 		var original = AsciiSet.create()
732 			.range('a', 'c')
733 			.build();
734 		var modified = original.copy()
735 			.range('d', 'f')
736 			.build();
737 		assertTrue(modified.contains('a'));
738 		assertTrue(modified.contains('b'));
739 		assertTrue(modified.contains('c'));
740 		assertTrue(modified.contains('d'));
741 		assertTrue(modified.contains('e'));
742 		assertTrue(modified.contains('f'));
743 		assertFalse(original.contains('d'));
744 	}
745 }
746