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.annotation;
18  
19  import static org.apache.juneau.junit.bct.BctAssertions.*;
20  import static org.junit.jupiter.api.Assertions.*;
21  
22  import java.lang.annotation.*;
23  import java.lang.reflect.*;
24  
25  import org.apache.juneau.*;
26  import org.apache.juneau.commons.reflect.*;
27  import org.junit.jupiter.api.*;
28  
29  class AppliedAnnotationObject_Test extends TestBase {
30  
31  	private static final String CNAME = AppliedAnnotationObject_Test.class.getName();
32  
33  	//------------------------------------------------------------------------------------------------------------------
34  	// Test annotation for testing purposes
35  	//------------------------------------------------------------------------------------------------------------------
36  
37  	@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR })
38  	@Retention(RetentionPolicy.RUNTIME)
39  	public @interface TA {
40  		String[] on() default {};
41  
42  		String value() default "";
43  
44  		int number() default 0;
45  	}
46  
47  	/**
48  	 * Implementation of TA using AppliedAnnotationObject with basic Builder
49  	 */
50  	public static class TAO extends AppliedAnnotationObject implements TA {
51  
52  		private final String value;
53  		private final int number;
54  
55  		public static class Builder extends AppliedAnnotationObject.Builder {
56  			String value = "";
57  			int number = 0;
58  
59  			public Builder() {
60  				super(TA.class);
61  			}
62  
63  			public Builder value(String value) {
64  				this.value = value;
65  				return this;
66  			}
67  
68  			public Builder number(int number) {
69  				this.number = number;
70  				return this;
71  			}
72  
73  			@Override
74  			public Builder on(String...value) {
75  				super.on(value);
76  				return this;
77  			}
78  
79  			public TA build() {
80  				return new TAO(this);
81  			}
82  		}
83  
84  		public static Builder create() {
85  			return new Builder();
86  		}
87  
88  		public TAO(Builder b) {
89  			super(b);
90  			value = b.value;
91  			number = b.number;
92  		}
93  
94  		@Override
95  		public String value() {
96  			return value;
97  		}
98  
99  		@Override
100 		public int number() {
101 			return number;
102 		}
103 	}
104 
105 	//------------------------------------------------------------------------------------------------------------------
106 	// Test classes for targeting
107 	//------------------------------------------------------------------------------------------------------------------
108 
109 	public static class TC {
110 		public String field1;
111 		public int field2;
112 
113 		public TC() {}
114 
115 		public TC(String s) {}
116 
117 		public void method1() {}
118 
119 		public String method2(int x) {
120 			return null;
121 		}
122 	}
123 
124 	public static class TC2 {}
125 
126 	//------------------------------------------------------------------------------------------------------------------
127 	// Nested test classes
128 	//------------------------------------------------------------------------------------------------------------------
129 
130 	@Nested
131 	@DisplayName("Basic on() tests with strings")
132 	class A_BasicOnTests extends TestBase {
133 
134 		@Test
135 		void a01_noTargets() {
136 			var a = TAO.create().build();
137 			assertList(a.on());
138 		}
139 
140 		@Test
141 		void a02_singleTarget() {
142 			var a = TAO.create().on("com.example.MyClass").build();
143 
144 			assertList(a.on(), "com.example.MyClass");
145 		}
146 
147 		@Test
148 		void a03_multipleTargets() {
149 			var a = TAO.create().on("com.example.Class1").on("com.example.Class2").build();
150 
151 			assertList(a.on(), "com.example.Class1", "com.example.Class2");
152 		}
153 
154 		@Test
155 		void a04_varargsTargets() {
156 			var a = TAO.create().on("target1", "target2", "target3").build();
157 
158 			assertList(a.on(), "target1", "target2", "target3");
159 		}
160 
161 		@Test
162 		void a05_withOtherProperties() {
163 			var a = TAO.create().on("com.example.MyClass").value("test").number(42).build();
164 
165 			assertBean(a, "on,value,number", "[com.example.MyClass],test,42");
166 		}
167 	}
168 
169 	@Nested
170 	@DisplayName("Equality and hashcode tests")
171 	class B_EqualityAndHashCodeTests extends TestBase {
172 
173 		@Test
174 		void b01_sameTargets() {
175 			var a1 = TAO.create().on("target1", "target2").value("test").build();
176 
177 			var a2 = TAO.create().on("target1", "target2").value("test").build();
178 
179 			assertEquals(a1, a2);
180 			assertEquals(a1.hashCode(), a2.hashCode());
181 		}
182 
183 		@Test
184 		void b02_differentTargets() {
185 			var a1 = TAO.create().on("target1").build();
186 
187 			var a2 = TAO.create().on("target2").build();
188 
189 			assertNotEquals(a1, a2);
190 		}
191 
192 		@Test
193 		void b03_differentTargetOrder() {
194 			// Arrays with different order should not be equal
195 			var a1 = TAO.create().on("target1", "target2").build();
196 
197 			var a2 = TAO.create().on("target2", "target1").build();
198 
199 			assertNotEquals(a1, a2);
200 		}
201 
202 		@Test
203 		void b04_noTargetsVsWithTargets() {
204 			var a1 = TAO.create().build();
205 			var a2 = TAO.create().on("target1").build();
206 
207 			assertNotEquals(a1, a2);
208 		}
209 	}
210 
211 	@Nested
212 	@DisplayName("BuilderT - Class targeting tests")
213 	class C_BuilderTTests extends TestBase {
214 
215 		/**
216 		 * Implementation with BuilderT for class targeting
217 		 */
218 		public static class C extends AppliedOnClassAnnotationObject implements TA {
219 
220 			private final String value;
221 
222 			public static class Builder extends AppliedAnnotationObject.BuilderT {
223 				String value = "";
224 
225 				public Builder() {
226 					super(TA.class);
227 				}
228 
229 				public Builder value(String value) {
230 					this.value = value;
231 					return this;
232 				}
233 
234 				@Override
235 				public Builder on(String...value) {
236 					super.on(value);
237 					return this;
238 				}
239 
240 				@Override
241 				public Builder on(Class<?>...value) {
242 					super.on(value);
243 					return this;
244 				}
245 
246 				@Override
247 				public Builder on(ClassInfo...value) {
248 					super.on(value);
249 					return this;
250 				}
251 
252 				@Override
253 				public Builder onClass(Class<?>...value) {
254 					super.onClass(value);
255 					return this;
256 				}
257 
258 				@Override
259 				public Builder onClass(ClassInfo...value) {
260 					super.onClass(value);
261 					return this;
262 				}
263 
264 				public TA build() {
265 					return new C(this);
266 				}
267 			}
268 
269 			public static Builder create() {
270 				return new Builder();
271 			}
272 
273 			public C(Builder b) {
274 				super(b);
275 				value = b.value;
276 			}
277 
278 			@Override
279 			public String value() {
280 				return value;
281 			}
282 
283 			@Override
284 			public int number() {
285 				return 0;
286 			}
287 		}
288 
289 		@Test
290 		void c01_onClassArray() {
291 			var a = C.create().on(TC.class, TC2.class).build();
292 
293 			assertList(a.on(), CNAME + "$TC", CNAME + "$TC2");
294 		}
295 
296 		@Test
297 		void c02_onClassInfo() {
298 			var ci1 = ClassInfo.of(TC.class);
299 			var ci2 = ClassInfo.of(TC2.class);
300 
301 			var a = C.create().on(ci1, ci2).build();
302 
303 			assertList(a.on(), CNAME + "$TC", CNAME + "$TC2");
304 		}
305 
306 		@Test
307 		void c03_mixedTargeting() {
308 			var a = C.create().on("com.example.StringTarget").on(TC.class).build();
309 
310 			assertList(a.on(), "com.example.StringTarget", CNAME + "$TC");
311 		}
312 
313 		@Test
314 		void c04_onClassClass() {
315 			var a = (AppliedOnClassAnnotationObject)C.create().onClass(TC.class, TC2.class).build();
316 
317 			assertList(a.onClass(), TC.class, TC2.class);
318 		}
319 
320 		@Test
321 		void c05_onClassClassInfo() {
322 			var ci1 = ClassInfo.of(TC.class);
323 			var ci2 = ClassInfo.of(TC2.class);
324 
325 			var a = (AppliedOnClassAnnotationObject)C.create().onClass(ci1, ci2).build();
326 
327 			assertList(a.onClass(), TC.class, TC2.class);
328 		}
329 	}
330 
331 	@Nested
332 	@DisplayName("BuilderM - Method targeting tests")
333 	class D_BuilderMTests extends TestBase {
334 
335 		/**
336 		 * Implementation with BuilderM for method targeting
337 		 */
338 		public static class D extends AppliedAnnotationObject implements TA {
339 
340 			public static class Builder extends AppliedAnnotationObject.BuilderM {
341 
342 				public Builder() {
343 					super(TA.class);
344 				}
345 
346 				@Override
347 				public Builder on(String...value) {
348 					super.on(value);
349 					return this;
350 				}
351 
352 				@Override
353 				public Builder on(Method...value) {
354 					super.on(value);
355 					return this;
356 				}
357 
358 				@Override
359 				public Builder on(MethodInfo...value) {
360 					super.on(value);
361 					return this;
362 				}
363 
364 				public TA build() {
365 					return new D(this);
366 				}
367 			}
368 
369 			public static Builder create() {
370 				return new Builder();
371 			}
372 
373 			public D(Builder b) {
374 				super(b);
375 			}
376 
377 			@Override
378 			public String value() {
379 				return "";
380 			}
381 
382 			@Override
383 			public int number() {
384 				return 0;
385 			}
386 		}
387 
388 		@Test
389 		void d01_onMethod() throws Exception {
390 			var m1 = TC.class.getMethod("method1");
391 			var m2 = TC.class.getMethod("method2", int.class);
392 
393 			var a = D.create().on(m1, m2).build();
394 
395 			assertList(a.on(), CNAME + "$TC.method1()", CNAME + "$TC.method2(int)");
396 		}
397 
398 		@Test
399 		void d02_onMethodInfo() throws Exception {
400 			var mi1 = MethodInfo.of(TC.class.getMethod("method1"));
401 			var mi2 = MethodInfo.of(TC.class.getMethod("method2", int.class));
402 
403 			var a = D.create().on(mi1, mi2).build();
404 
405 			assertList(a.on(), CNAME + "$TC.method1()", CNAME + "$TC.method2(int)");
406 		}
407 	}
408 
409 	@Nested
410 	@DisplayName("BuilderC - Constructor targeting tests")
411 	class E_BuilderCTests extends TestBase {
412 
413 		/**
414 		 * Implementation with BuilderC for constructor targeting
415 		 */
416 		public static class E extends AppliedAnnotationObject implements TA {
417 
418 			public static class Builder extends AppliedAnnotationObject.BuilderC {
419 
420 				public Builder() {
421 					super(TA.class);
422 				}
423 
424 				@Override
425 				public Builder on(String...value) {
426 					super.on(value);
427 					return this;
428 				}
429 
430 				@Override
431 				public Builder on(Constructor<?>...value) {
432 					super.on(value);
433 					return this;
434 				}
435 
436 				@Override
437 				public Builder on(ConstructorInfo...value) {
438 					super.on(value);
439 					return this;
440 				}
441 
442 				public TA build() {
443 					return new E(this);
444 				}
445 			}
446 
447 			public static Builder create() {
448 				return new Builder();
449 			}
450 
451 			public E(Builder b) {
452 				super(b);
453 			}
454 
455 			@Override
456 			public String value() {
457 				return "";
458 			}
459 
460 			@Override
461 			public int number() {
462 				return 0;
463 			}
464 		}
465 
466 		@Test
467 		void e01_onConstructor() throws Exception {
468 			var c1 = TC.class.getConstructor();
469 			var c2 = TC.class.getConstructor(String.class);
470 
471 			var a = E.create().on(c1, c2).build();
472 
473 			assertList(a.on(), CNAME + "$TC()", CNAME + "$TC(java.lang.String)");
474 		}
475 
476 		@Test
477 		void e02_onConstructorInfo() throws Exception {
478 			var ci1 = ConstructorInfo.of(TC.class.getConstructor());
479 			var ci2 = ConstructorInfo.of(TC.class.getConstructor(String.class));
480 
481 			var a = E.create().on(ci1, ci2).build();
482 
483 			assertList(a.on(), CNAME + "$TC()", CNAME + "$TC(java.lang.String)");
484 		}
485 	}
486 
487 	@Nested
488 	@DisplayName("BuilderMF - Method and Field targeting tests")
489 	class F_BuilderMFTests extends TestBase {
490 
491 		/**
492 		 * Implementation with BuilderMF for method and field targeting
493 		 */
494 		public static class F extends AppliedAnnotationObject implements TA {
495 
496 			public static class Builder extends AppliedAnnotationObject.BuilderMF {
497 
498 				public Builder() {
499 					super(TA.class);
500 				}
501 
502 				@Override
503 				public Builder on(String...value) {
504 					super.on(value);
505 					return this;
506 				}
507 
508 				@Override
509 				public Builder on(Method...value) {
510 					super.on(value);
511 					return this;
512 				}
513 
514 				@Override
515 				public Builder on(MethodInfo...value) {
516 					super.on(value);
517 					return this;
518 				}
519 
520 				@Override
521 				public Builder on(Field...value) {
522 					super.on(value);
523 					return this;
524 				}
525 
526 				@Override
527 				public Builder on(FieldInfo...value) {
528 					super.on(value);
529 					return this;
530 				}
531 
532 				public TA build() {
533 					return new F(this);
534 				}
535 			}
536 
537 			public static Builder create() {
538 				return new Builder();
539 			}
540 
541 			public F(Builder b) {
542 				super(b);
543 			}
544 
545 			@Override
546 			public String value() {
547 				return "";
548 			}
549 
550 			@Override
551 			public int number() {
552 				return 0;
553 			}
554 		}
555 
556 		@Test
557 		void f01_onField() throws Exception {
558 			var f1 = TC.class.getField("field1");
559 			var f2 = TC.class.getField("field2");
560 
561 			var a = F.create().on(f1, f2).build();
562 
563 			assertList(a.on(), CNAME + "$TC.field1", CNAME + "$TC.field2");
564 		}
565 
566 		@Test
567 		void f02_onFieldInfo() throws Exception {
568 			var fi1 = FieldInfo.of(ClassInfo.of(TC.class), TC.class.getField("field1"));
569 			var fi2 = FieldInfo.of(ClassInfo.of(TC.class), TC.class.getField("field2"));
570 
571 			var a = F.create().on(fi1, fi2).build();
572 
573 			assertList(a.on(), CNAME + "$TC.field1", CNAME + "$TC.field2");
574 		}
575 
576 		@Test
577 		void f03_mixedMethodsAndFields() throws Exception {
578 			var m = TC.class.getMethod("method1");
579 			var f = TC.class.getField("field1");
580 
581 			var a = F.create().on(m).on(f).build();
582 
583 			assertList(a.on(), CNAME + "$TC.method1()", CNAME + "$TC.field1");
584 		}
585 
586 		@Test
587 		void f04_onMethodInfo() throws Exception {
588 			var mi1 = MethodInfo.of(TC.class.getMethod("method1"));
589 			var mi2 = MethodInfo.of(TC.class.getMethod("method2", int.class));
590 
591 			var a = F.create().on(mi1, mi2).build();
592 
593 			assertList(a.on(), CNAME + "$TC.method1()", CNAME + "$TC.method2(int)");
594 		}
595 	}
596 
597 	@Nested
598 	@DisplayName("BuilderTM - Class and Method targeting tests")
599 	class G_BuilderTMTests extends TestBase {
600 
601 		/**
602 		 * Implementation with BuilderTM for class and method targeting
603 		 */
604 		public static class G extends AppliedOnClassAnnotationObject implements TA {
605 
606 			public static class Builder extends AppliedAnnotationObject.BuilderTM {
607 
608 				public Builder() {
609 					super(TA.class);
610 				}
611 
612 				@Override
613 				public Builder on(String...value) {
614 					super.on(value);
615 					return this;
616 				}
617 
618 				@Override
619 				public Builder on(Class<?>...value) {
620 					super.on(value);
621 					return this;
622 				}
623 
624 				@Override
625 				public Builder on(ClassInfo...value) {
626 					super.on(value);
627 					return this;
628 				}
629 
630 				@Override
631 				public Builder on(Method...value) {
632 					super.on(value);
633 					return this;
634 				}
635 
636 				@Override
637 				public Builder on(MethodInfo...value) {
638 					super.on(value);
639 					return this;
640 				}
641 
642 				@Override
643 				public Builder onClass(Class<?>...value) {
644 					super.onClass(value);
645 					return this;
646 				}
647 
648 				public TA build() {
649 					return new G(this);
650 				}
651 			}
652 
653 			public static Builder create() {
654 				return new Builder();
655 			}
656 
657 			public G(Builder b) {
658 				super(b);
659 			}
660 
661 			@Override
662 			public String value() {
663 				return "";
664 			}
665 
666 			@Override
667 			public int number() {
668 				return 0;
669 			}
670 		}
671 
672 		@Test
673 		void g01_onClassAndMethod() throws Exception {
674 			var m = TC.class.getMethod("method1");
675 
676 			var a = G.create().on(TC.class).on(m).build();
677 
678 			assertList(a.on(), CNAME + "$TC", CNAME + "$TC.method1()");
679 		}
680 
681 		@Test
682 		void g02_onMethodInfo() throws Exception {
683 			var mi = MethodInfo.of(TC.class.getMethod("method1"));
684 
685 			var a = G.create().on(mi).build();
686 
687 			assertList(a.on(), CNAME + "$TC.method1()");
688 		}
689 
690 		@Test
691 		void g03_onClassClass() {
692 			var a = (AppliedOnClassAnnotationObject) G.create().onClass(TC.class, TC2.class).build();
693 
694 			assertList(a.onClass(), TC.class, TC2.class);
695 		}
696 	}
697 
698 	@Nested
699 	@DisplayName("BuilderTMF - Class, Method, and Field targeting tests")
700 	class H_BuilderTMFTests extends TestBase {
701 
702 		/**
703 		 * Implementation with BuilderTMF for class, method, and field targeting
704 		 */
705 		public static class H extends AppliedOnClassAnnotationObject implements TA {
706 
707 			public static class Builder extends AppliedAnnotationObject.BuilderTMF {
708 
709 				public Builder() {
710 					super(TA.class);
711 				}
712 
713 				@Override
714 				public Builder on(String...value) {
715 					super.on(value);
716 					return this;
717 				}
718 
719 				@Override
720 				public Builder on(Class<?>...value) {
721 					super.on(value);
722 					return this;
723 				}
724 
725 				@Override
726 				public Builder on(ClassInfo...value) {
727 					super.on(value);
728 					return this;
729 				}
730 
731 				@Override
732 				public Builder on(Method...value) {
733 					super.on(value);
734 					return this;
735 				}
736 
737 				@Override
738 				public Builder on(MethodInfo...value) {
739 					super.on(value);
740 					return this;
741 				}
742 
743 				@Override
744 				public Builder on(Field...value) {
745 					super.on(value);
746 					return this;
747 				}
748 
749 				@Override
750 				public Builder on(FieldInfo...value) {
751 					super.on(value);
752 					return this;
753 				}
754 
755 				public TA build() {
756 					return new H(this);
757 				}
758 			}
759 
760 			public static Builder create() {
761 				return new Builder();
762 			}
763 
764 			public H(Builder b) {
765 				super(b);
766 			}
767 
768 			@Override
769 			public String value() {
770 				return "";
771 			}
772 
773 			@Override
774 			public int number() {
775 				return 0;
776 			}
777 		}
778 
779 		@Test
780 		void h01_onClassMethodAndField() throws Exception {
781 			var m = TC.class.getMethod("method1");
782 			var f = TC.class.getField("field1");
783 
784 			var a = H.create().on(TC.class).on(m).on(f).build();
785 
786 			assertList(a.on(), CNAME + "$TC", CNAME + "$TC.method1()", CNAME + "$TC.field1");
787 		}
788 
789 		@Test
790 		void h02_onMethodInfo() throws Exception {
791 			var mi = MethodInfo.of(TC.class.getMethod("method1"));
792 
793 			var a = H.create().on(mi).build();
794 
795 			assertList(a.on(), CNAME + "$TC.method1()");
796 		}
797 
798 		@Test
799 		void h03_onFieldInfo() throws Exception {
800 			var fi = FieldInfo.of(ClassInfo.of(TC.class), TC.class.getField("field1"));
801 
802 			var a = H.create().on(fi).build();
803 
804 			assertList(a.on(), CNAME + "$TC.field1");
805 		}
806 	}
807 
808 	@Nested
809 	@DisplayName("BuilderTMFC - Class, Method, Field, and Constructor targeting tests")
810 	class I_BuilderTMFCTests extends TestBase {
811 
812 		/**
813 		 * Implementation with BuilderTMFC for complete targeting
814 		 */
815 		public static class I extends AppliedOnClassAnnotationObject implements TA {
816 
817 			public static class Builder extends AppliedAnnotationObject.BuilderTMFC {
818 
819 				public Builder() {
820 					super(TA.class);
821 				}
822 
823 				@Override
824 				public Builder on(String...value) {
825 					super.on(value);
826 					return this;
827 				}
828 
829 				@Override
830 				public Builder on(Class<?>...value) {
831 					super.on(value);
832 					return this;
833 				}
834 
835 				@Override
836 				public Builder on(Method...value) {
837 					super.on(value);
838 					return this;
839 				}
840 
841 				@Override
842 				public Builder on(MethodInfo...value) {
843 					super.on(value);
844 					return this;
845 				}
846 
847 				@Override
848 				public Builder on(Field...value) {
849 					super.on(value);
850 					return this;
851 				}
852 
853 				@Override
854 				public Builder on(FieldInfo...value) {
855 					super.on(value);
856 					return this;
857 				}
858 
859 				@Override
860 				public Builder on(Constructor<?>...value) {
861 					super.on(value);
862 					return this;
863 				}
864 
865 				@Override
866 				public Builder on(ConstructorInfo...value) {
867 					super.on(value);
868 					return this;
869 				}
870 
871 				public TA build() {
872 					return new I(this);
873 				}
874 			}
875 
876 			public static Builder create() {
877 				return new Builder();
878 			}
879 
880 			public I(Builder b) {
881 				super(b);
882 			}
883 
884 			@Override
885 			public String value() {
886 				return "";
887 			}
888 
889 			@Override
890 			public int number() {
891 				return 0;
892 			}
893 		}
894 
895 		@Test
896 		void i01_onAllTypes() throws Exception {
897 			var m = TC.class.getMethod("method1");
898 			var f = TC.class.getField("field1");
899 			var c = TC.class.getConstructor();
900 
901 			var a = I.create().on(TC.class).on(m).on(f).on(c).build();
902 
903 			assertList(a.on(), CNAME + "$TC", CNAME + "$TC.method1()", CNAME + "$TC.field1", CNAME + "$TC()");
904 		}
905 
906 		@Test
907 		void i02_onMethodInfo() throws Exception {
908 			var mi = MethodInfo.of(TC.class.getMethod("method1"));
909 
910 			var a = I.create().on(mi).build();
911 
912 			assertList(a.on(), CNAME + "$TC.method1()");
913 		}
914 
915 		@Test
916 		void i03_onFieldInfo() throws Exception {
917 			var fi = FieldInfo.of(ClassInfo.of(TC.class), TC.class.getField("field1"));
918 
919 			var a = I.create().on(fi).build();
920 
921 			assertList(a.on(), CNAME + "$TC.field1");
922 		}
923 
924 		@Test
925 		void i04_onConstructorInfo() throws Exception {
926 			var ci = ConstructorInfo.of(TC.class.getConstructor(String.class));
927 
928 			var a = I.create().on(ci).build();
929 
930 			assertList(a.on(), CNAME + "$TC(java.lang.String)");
931 		}
932 	}
933 
934 	@Nested
935 	@DisplayName("Fluent API tests")
936 	class J_FluentApiTests extends TestBase {
937 
938 		@Test
939 		void j01_chaining() {
940 			var a = TAO.create().on("target1").value("test").on("target2").number(42).on("target3").build();
941 
942 			assertBean(a, "on,value,number", "[target1,target2,target3],test,42");
943 		}
944 	}
945 
946 	@Nested
947 	@DisplayName("toMap() tests")
948 	class K_ToMapTests extends TestBase {
949 
950 		@Test
951 		void k01_withTargets() {
952 			var a = TAO.create().on("target1", "target2").value("test").build();
953 
954 			var map = ((TAO)a).propertyMap();
955 			assertBean(map, "on,value", "[target1,target2],test");
956 		}
957 	}
958 
959 	@Nested
960 	@DisplayName("Edge case tests")
961 	class L_EdgeCaseTests extends TestBase {
962 
963 		@Test
964 		void l01_emptyTargets() {
965 			var a = TAO.create().on().build();
966 
967 			assertList(a.on());
968 		}
969 
970 		@Test
971 		void l02_builderReuse() {
972 			var builder = TAO.create().on("target1").value("test");
973 
974 			var a1 = builder.build();
975 			var a2 = builder.build();
976 
977 			// Different instances but equal
978 			assertNotSame(a1, a2);
979 			assertEquals(a1, a2);
980 		}
981 	}
982 
983 	@Nested
984 	@DisplayName("Null validation tests")
985 	class M_NullValidationTests extends TestBase {
986 
987 		@Test
988 		void m01_nullClass_throwsException() {
989 			assertThrows(IllegalArgumentException.class, () -> C_BuilderTTests.C.create().on((Class<?>)null).build());
990 		}
991 
992 		@Test
993 		void m02_nullClassInfo_throwsException() {
994 			assertThrows(IllegalArgumentException.class, () -> C_BuilderTTests.C.create().on((ClassInfo)null).build());
995 		}
996 
997 		@Test
998 		void m03_nullMethod_throwsException() {
999 			assertThrows(IllegalArgumentException.class, () -> D_BuilderMTests.D.create().on((java.lang.reflect.Method)null).build());
1000 		}
1001 
1002 		@Test
1003 		void m04_nullMethodInfo_throwsException() {
1004 			assertThrows(IllegalArgumentException.class, () -> D_BuilderMTests.D.create().on((MethodInfo)null).build());
1005 		}
1006 
1007 		@Test
1008 		void m05_nullField_throwsException() {
1009 			assertThrows(IllegalArgumentException.class, () -> F_BuilderMFTests.F.create().on((java.lang.reflect.Field)null).build());
1010 		}
1011 
1012 		@Test
1013 		void m06_nullFieldInfo_throwsException() {
1014 			assertThrows(IllegalArgumentException.class, () -> F_BuilderMFTests.F.create().on((FieldInfo)null).build());
1015 		}
1016 
1017 		@Test
1018 		void m07_nullConstructor_throwsException() {
1019 			assertThrows(IllegalArgumentException.class, () -> E_BuilderCTests.E.create().on((java.lang.reflect.Constructor<?>)null).build());
1020 		}
1021 
1022 		@Test
1023 		void m08_nullConstructorInfo_throwsException() {
1024 			assertThrows(IllegalArgumentException.class, () -> E_BuilderCTests.E.create().on((ConstructorInfo)null).build());
1025 		}
1026 	}
1027 }