This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 18059aa114 Unit tests
18059aa114 is described below

commit 18059aa114855dcd00d9cd0c7c58c5f348c2c28e
Author: James Bognar <[email protected]>
AuthorDate: Tue Dec 2 08:35:08 2025 -0800

    Unit tests
---
 .../juneau/commons/reflect/AnnotationInfo.java     |    2 +-
 .../juneau/commons/reflect/AnnotationProvider.java |   10 +-
 .../juneau/commons/utils/AssertionUtils.java       |   59 +
 .../commons/reflect/AnnotationInfo_Test.java       |  102 ++
 .../commons/reflect/AnnotationProvider_Test.java   | 1137 ++++++++++++++++++++
 5 files changed, 1302 insertions(+), 8 deletions(-)

diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/AnnotationInfo.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/AnnotationInfo.java
index 2f894be1d4..7c9047e145 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/AnnotationInfo.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/AnnotationInfo.java
@@ -90,7 +90,7 @@ public class AnnotationInfo<T extends Annotation> {
        private static int findRank(Object a) {
                // @formatter:off
                return ClassInfo.of(a).getAllMethods().stream()
-                       .filter(m -> m.hasName("rank") && m.getParameterCount() 
== 0 && m.hasReturnType(int.class))
+                       .filter(m -> m.hasName("rank") && 
m.hasReturnType(int.class))
                        .findFirst()
                        .map(m -> safe(() -> (int)m.invoke(a)))
                        .orElse(0);
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/AnnotationProvider.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/AnnotationProvider.java
index befabb8781..2c019dd8c3 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/AnnotationProvider.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/AnnotationProvider.java
@@ -933,10 +933,6 @@ public class AnnotationProvider {
                return ! find(type, m, traversals).isEmpty();
        }
 
-       
//-----------------------------------------------------------------------------------------------------------------
-       // Helper methods
-       
//-----------------------------------------------------------------------------------------------------------------
-
        /**
         * Checks if a parameter has the specified annotation.
         *
@@ -998,10 +994,10 @@ public class AnnotationProvider {
                        t = l(a(SELF, MATCHING_METHODS, DECLARING_CLASS, 
RETURN_TYPE, PACKAGE));
                else if (element instanceof FieldInfo || element instanceof 
ConstructorInfo)
                        t = l(a(SELF));
-               else if (element instanceof ParameterInfo)
+               else {
+                       assertType(ParameterInfo.class, element, () -> 
unsupportedOp());
                        t = l(a(SELF, MATCHING_PARAMETERS, PARAMETER_TYPE));
-               else
-                       t = l();  // Never happens.
+               }
 
                if (element instanceof ClassInfo element2) {
                        if (t.contains(SELF)) {
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/AssertionUtils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/AssertionUtils.java
index 28a0af20d6..9d2e9012bf 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/AssertionUtils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/AssertionUtils.java
@@ -212,6 +212,65 @@ public class AssertionUtils {
                assertArg(o5 != null, "Argument ''{0}'' cannot be null.", 
name5);
        }
 
+       /**
+        * Throws an {@link IllegalArgumentException} if the specified object 
is not an instance of the specified type.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jk>import static</jk> 
org.apache.juneau.commons.utils.AssertionUtils.*;
+        *
+        *      <jk>public</jk> <jk>void</jk> setValue(Object <jv>value</jv>) {
+        *              <jc>// Validate that value is a String</jc>
+        *              <jsm>assertType</jsm>(String.<jk>class</jk>, 
<jv>value</jv>);
+        *              ...
+        *      }
+        * </p>
+        *
+        * @param <T> The expected type.
+        * @param type The expected class type.
+        * @param o The object to check.
+        * @return The object cast to the specified type.
+        * @throws IllegalArgumentException Thrown if the object is not an 
instance of the specified type.
+        */
+       @SuppressWarnings("unchecked")
+       public static final <T> T assertType(Class<T> type, Object o) throws 
IllegalArgumentException {
+               assertArg(type != null, "Type cannot be null.");
+               assertArg(o != null, "Object cannot be null.");
+               if (! type.isInstance(o))
+                       throw illegalArg("Object is not an instance of {0}: 
{1}", cn(type), cn(o));
+               return (T)o;
+       }
+
+       /**
+        * Throws the exception provided by the supplier if the specified 
object is not an instance of the specified type.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jk>import static</jk> 
org.apache.juneau.commons.utils.AssertionUtils.*;
+        *
+        *      <jk>public</jk> <jk>void</jk> setValue(Object <jv>value</jv>) {
+        *              <jc>// Validate that value is a String, throw custom 
exception</jc>
+        *              <jsm>assertType</jsm>(String.<jk>class</jk>, 
<jv>value</jv>, () -&gt; <jk>new</jk> IllegalStateException(<js>"Invalid value 
type"</js>));
+        *              ...
+        *      }
+        * </p>
+        *
+        * @param <T> The expected type.
+        * @param type The expected class type.
+        * @param o The object to check.
+        * @param exceptionSupplier The supplier that provides the exception to 
throw if validation fails.
+        * @return The object cast to the specified type.
+        * @throws RuntimeException Thrown if the object is not an instance of 
the specified type (the exception is provided by the supplier).
+        */
+       @SuppressWarnings("unchecked")
+       public static final <T> T assertType(Class<T> type, Object o, 
java.util.function.Supplier<? extends RuntimeException> exceptionSupplier) 
throws RuntimeException {
+               assertArg(type != null, "Type cannot be null.");
+               assertArg(o != null, "Object cannot be null.");
+               if (! type.isInstance(o))
+                       throw exceptionSupplier.get();
+               return (T)o;
+       }
+
        /**
         * Throws an {@link IllegalArgumentException} if the specified value 
doesn't have all subclasses of the specified type.
         *
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/AnnotationInfo_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/AnnotationInfo_Test.java
index fe27bf4730..627a49800c 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/AnnotationInfo_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/AnnotationInfo_Test.java
@@ -73,6 +73,12 @@ class AnnotationInfo_Test extends TestBase {
                String value() default "";
        }
 
+       @Target(TYPE)
+       @Retention(RUNTIME)
+       public static @interface RankWithWrongReturnTypeAnnotation {
+               String rank() default "";  // Returns String, not int, should 
not match
+       }
+
        @Target(TYPE)
        @Retention(RUNTIME)
        public static @interface ClassArrayAnnotation {
@@ -122,6 +128,9 @@ class AnnotationInfo_Test extends TestBase {
        @UnrankedAnnotation
        public static class UnrankedClass {}
 
+       @RankWithWrongReturnTypeAnnotation
+       public static class RankWithWrongReturnTypeClass {}
+
        @ClassArrayAnnotation(classes = { String.class, Integer.class })
        public static class ClassArrayClass {}
 
@@ -277,6 +286,15 @@ class AnnotationInfo_Test extends TestBase {
                assertEquals(0, ai.getRank());
        }
 
+       @Test
+       void f03_getRank_withRankMethodWrongReturnType_returnsZero() {
+               // rank() method returns String, not int, so it doesn't match 
the filter
+               var ci = ClassInfo.of(RankWithWrongReturnTypeClass.class);
+               var ai = 
ci.getAnnotations(RankWithWrongReturnTypeAnnotation.class).findFirst().orElse(null);
+               assertNotNull(ai);
+               assertEquals(0, ai.getRank());
+       }
+
        
//====================================================================================================
        // getMethod()
        
//====================================================================================================
@@ -656,6 +674,18 @@ class AnnotationInfo_Test extends TestBase {
        // toMap()
        
//====================================================================================================
 
+       @Target(TYPE)
+       @Retention(RUNTIME)
+       public static @interface ToMapTestAnnotation {
+               String value() default "default";
+               String[] arrayValue() default {};
+               String[] nonEmptyArray() default {"a", "b"};
+               String[] emptyArrayWithNonEmptyDefault() default {"default"};
+       }
+
+       @ToMapTestAnnotation(value = "custom", arrayValue = {}, nonEmptyArray = 
{"x"}, emptyArrayWithNonEmptyDefault = {})
+       public static class ToMapTestClass {}
+
        @Test
        void s01_toMap_returnsMapRepresentation() {
                var ci = ClassInfo.of(TestClass.class);
@@ -672,6 +702,78 @@ class AnnotationInfo_Test extends TestBase {
                assertEquals("test", annotationMap.get("value"));
        }
 
+       @Test
+       void s02_toMap_withNonDefaultValues_includesOnlyNonDefaults() {
+               var ci = ClassInfo.of(ToMapTestClass.class);
+               var ai = 
ci.getAnnotations(ToMapTestAnnotation.class).findFirst().orElse(null);
+               assertNotNull(ai);
+
+               var map = ai.toMap();
+               assertNotNull(map);
+               var annotationMap = 
(java.util.Map<String,Object>)map.get("@ToMapTestAnnotation");
+               assertNotNull(annotationMap);
+               
+               // value differs from default (non-array), should be included - 
covers branch: isArray(v) = false
+               assertEquals("custom", annotationMap.get("value"));
+               
+               // nonEmptyArray differs from default (non-empty array), should 
be included - covers branch: isArray(v) = true, length(v) != 0
+               assertTrue(annotationMap.containsKey("nonEmptyArray"));
+               
+               // emptyArrayWithNonEmptyDefault is empty array but default is 
non-empty, should be included - covers branch: isArray(v) = true, length(v) == 
0, length(d) != 0
+               
assertTrue(annotationMap.containsKey("emptyArrayWithNonEmptyDefault"));
+               
+               // arrayValue is empty array matching default empty array, 
should NOT be included - covers branch: isArray(v) = true, length(v) == 0, 
length(d) == 0
+               assertFalse(annotationMap.containsKey("arrayValue"));
+       }
+
+       @Test
+       void s03_toMap_withExceptionHandling_handlesException() {
+               // Create a runtime annotation proxy that throws an exception 
when a method is invoked
+               var annotationType = ToMapTestAnnotation.class;
+               var handler = new java.lang.reflect.InvocationHandler() {
+                       @Override
+                       public Object invoke(Object proxy, 
java.lang.reflect.Method method, Object[] args) throws Throwable {
+                               if (method.getName().equals("value")) {
+                                       throw new RuntimeException("Test 
exception");
+                               }
+                               if (method.getName().equals("annotationType")) {
+                                       return annotationType;
+                               }
+                               if (method.getName().equals("toString")) {
+                                       return "@ToMapTestAnnotation";
+                               }
+                               if (method.getName().equals("hashCode")) {
+                                       return 0;
+                               }
+                               if (method.getName().equals("equals")) {
+                                       return false;
+                               }
+                               return method.getDefaultValue();
+                       }
+               };
+               
+               var proxyAnnotation = 
(ToMapTestAnnotation)java.lang.reflect.Proxy.newProxyInstance(
+                       annotationType.getClassLoader(),
+                       new Class[]{annotationType},
+                       handler
+               );
+               
+               var ci = ClassInfo.of(ToMapTestClass.class);
+               var ai = AnnotationInfo.of(ci, proxyAnnotation);
+               
+               var map = ai.toMap();
+               assertNotNull(map);
+               var annotationMap = 
(java.util.Map<String,Object>)map.get("@ToMapTestAnnotation");
+               assertNotNull(annotationMap);
+               
+               // The exception should be caught and stored as a localized 
message
+               assertTrue(annotationMap.containsKey("value"));
+               var value = annotationMap.get("value");
+               assertNotNull(value);
+               // The value should be a string representation of the exception
+               assertTrue(value instanceof String);
+       }
+
        
//====================================================================================================
        // toSimpleString()
        
//====================================================================================================
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/AnnotationProvider_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/AnnotationProvider_Test.java
new file mode 100644
index 0000000000..33b320a588
--- /dev/null
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/AnnotationProvider_Test.java
@@ -0,0 +1,1137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.commons.reflect;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+import static org.apache.juneau.commons.reflect.AnnotationTraversal.*;
+import static org.apache.juneau.commons.utils.CollectionUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.lang.annotation.*;
+import org.apache.juneau.*;
+import org.apache.juneau.commons.collections.*;
+import org.junit.jupiter.api.*;
+
+class AnnotationProvider_Test extends TestBase {
+
+       
//====================================================================================================
+       // Test annotations and classes
+       
//====================================================================================================
+
+       @Target(TYPE)
+       @Retention(RUNTIME)
+       public static @interface TestAnnotation {
+               String value() default "default";
+       }
+
+       @Target({TYPE, METHOD, FIELD, CONSTRUCTOR, PARAMETER})
+       @Retention(RUNTIME)
+       public static @interface MultiTargetAnnotation {
+               int value() default 0;
+       }
+
+       @Target(TYPE)
+       @Retention(RUNTIME)
+       public static @interface ParentAnnotation {
+               String value() default "";
+       }
+
+       @TestAnnotation("class")
+       public static class TestClass {
+               @MultiTargetAnnotation(1)
+               public String field1;
+
+               @MultiTargetAnnotation(2)
+               public TestClass() {}
+
+               @MultiTargetAnnotation(3)
+               public void method1() {}
+
+               public void method2(@MultiTargetAnnotation(4) String param) {}
+       }
+
+       @ParentAnnotation("parent")
+       public static class ParentClass {}
+
+       @TestAnnotation("child")
+       public static class ChildClass extends ParentClass {}
+
+       // Test classes for MATCHING_METHODS traversal
+       public static interface MatchingMethodInterface {
+               @MultiTargetAnnotation(10)
+               void matchingMethod(String param);
+       }
+
+       public static class MatchingMethodParent {
+               @MultiTargetAnnotation(20)
+               public void matchingMethod(String param) {}
+       }
+
+       public static class MatchingMethodChild extends MatchingMethodParent 
implements MatchingMethodInterface {
+               @MultiTargetAnnotation(30)
+               @Override
+               public void matchingMethod(String param) {}
+       }
+
+       // Test classes for MATCHING_PARAMETERS traversal
+       public static interface MatchingParameterInterface {
+               void matchingParameterMethod(@MultiTargetAnnotation(100) String 
param);
+       }
+
+       public static class MatchingParameterParent {
+               public void matchingParameterMethod(@MultiTargetAnnotation(200) 
String param) {}
+       }
+
+       public static class MatchingParameterChild extends 
MatchingParameterParent implements MatchingParameterInterface {
+               @Override
+               public void matchingParameterMethod(@MultiTargetAnnotation(300) 
String param) {}
+       }
+
+       
//====================================================================================================
+       // create() and INSTANCE
+       
//====================================================================================================
+
+       @Test
+       void a01_create_returnsBuilder() {
+               var builder = AnnotationProvider.create();
+               assertNotNull(builder);
+               var provider = builder.build();
+               assertNotNull(provider);
+       }
+
+       @Test
+       void a02_instance_isNotNull() {
+               assertNotNull(AnnotationProvider.INSTANCE);
+       }
+
+       
//====================================================================================================
+       // find(Class, ClassInfo, ...) - typed find for classes
+       
//====================================================================================================
+
+       @Test
+       void b01_find_typedClass_returnsAnnotations() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var annotations = provider.find(TestAnnotation.class, ci, SELF);
+               
+               assertNotNull(annotations);
+               assertEquals(1, annotations.size());
+               assertEquals("class", 
annotations.get(0).getValue().orElse(null));
+       }
+
+       @Test
+       void b02_find_typedClass_withParents_returnsAnnotationsFromHierarchy() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(ChildClass.class);
+               var annotations = provider.find(TestAnnotation.class, ci, SELF, 
PARENTS);
+               
+               assertNotNull(annotations);
+               // Should find annotation on child class
+               assertTrue(annotations.size() >= 1);
+               var childAnnotation = annotations.stream()
+                       .filter(a -> a.getValue().orElse("").equals("child"))
+                       .findFirst();
+               assertTrue(childAnnotation.isPresent());
+       }
+
+       @Test
+       void b03_find_typedClass_notFound_returnsEmptyList() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var annotations = provider.find(ParentAnnotation.class, ci, 
SELF);
+               
+               assertNotNull(annotations);
+               assertTrue(annotations.isEmpty());
+       }
+
+       @Test
+       void b04_find_typedClass_withNullType_throwsException() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               assertThrows(IllegalArgumentException.class, () -> 
provider.find(null, ci, SELF));
+       }
+
+       @Test
+       void b05_find_typedClass_withNullClassInfo_throwsException() {
+               var provider = AnnotationProvider.create().build();
+               ClassInfo nullClassInfo = null;
+               assertThrows(IllegalArgumentException.class, () -> 
provider.find(TestAnnotation.class, nullClassInfo, SELF));
+       }
+
+       
//====================================================================================================
+       // find(ClassInfo, ...) - untyped find for classes
+       
//====================================================================================================
+
+       @Test
+       void c01_find_untypedClass_returnsAllAnnotations() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var annotations = provider.find(ci, SELF);
+               
+               assertNotNull(annotations);
+               assertTrue(annotations.size() >= 1);
+               assertTrue(annotations.stream().anyMatch(a -> 
a.isType(TestAnnotation.class)));
+       }
+
+       @Test
+       void c02_find_untypedClass_withNullClassInfo_throwsException() {
+               var provider = AnnotationProvider.create().build();
+               assertThrows(IllegalArgumentException.class, () -> 
provider.find((ClassInfo)null, SELF));
+       }
+
+       
//====================================================================================================
+       // find(Class, FieldInfo, ...) - typed find for fields
+       
//====================================================================================================
+
+       @Test
+       void d01_find_typedField_returnsAnnotations() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var field = ci.getPublicField(x -> 
x.hasName("field1")).orElse(null);
+               assertNotNull(field);
+               
+               var annotations = provider.find(MultiTargetAnnotation.class, 
field, SELF);
+               
+               assertNotNull(annotations);
+               assertEquals(1, annotations.size());
+               assertEquals(1, annotations.get(0).getInt("value").orElse(0));
+       }
+
+       @Test
+       void 
d03_find_typedField_withRuntimeAnnotations_includesRuntimeAndDeclared() {
+               // This test covers lines 1039-1041 - SELF traversal for 
FieldInfo with runtime annotations
+               var runtimeAnnotation = new RuntimeOnAnnotation(new 
String[]{"org.apache.juneau.commons.reflect.AnnotationProvider_Test$TestClass.field1"},
 "runtimeField");
+               var provider = AnnotationProvider.create()
+                       .addRuntimeAnnotations(runtimeAnnotation)
+                       .build();
+               var ci = ClassInfo.of(TestClass.class);
+               var field = ci.getPublicField(x -> 
x.hasName("field1")).orElse(null);
+               assertNotNull(field);
+               
+               // Call with SELF traversal - should include both runtime 
annotations (line 1040) and declared annotations (line 1041)
+               var annotations = provider.find(TestAnnotation.class, field, 
SELF);
+               
+               assertNotNull(annotations);
+               // Should find both the runtime annotation and the declared 
annotation (if any)
+               assertTrue(annotations.size() >= 1, "Should find at least the 
runtime annotation");
+               
+               // Verify runtime annotation is found
+               var runtimeAnnotationFound = annotations.stream()
+                       .filter(a -> 
a.getValue().orElse("").equals("runtimeField"))
+                       .findFirst();
+               assertTrue(runtimeAnnotationFound.isPresent(), "Should find 
runtime annotation on field");
+       }
+
+       @Test
+       void d02_find_typedField_notFound_returnsEmptyList() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var field = ci.getPublicField(x -> 
x.hasName("field1")).orElse(null);
+               assertNotNull(field);
+               
+               var annotations = provider.find(TestAnnotation.class, field, 
SELF);
+               
+               assertNotNull(annotations);
+               assertTrue(annotations.isEmpty());
+       }
+
+       
//====================================================================================================
+       // find(FieldInfo, ...) - untyped find for fields
+       
//====================================================================================================
+
+       @Test
+       void e01_find_untypedField_returnsAllAnnotations() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var field = ci.getPublicField(x -> 
x.hasName("field1")).orElse(null);
+               assertNotNull(field);
+               
+               var annotations = provider.find(field, SELF);
+               
+               assertNotNull(annotations);
+               assertTrue(annotations.size() >= 1);
+               assertTrue(annotations.stream().anyMatch(a -> 
a.isType(MultiTargetAnnotation.class)));
+       }
+
+       
//====================================================================================================
+       // find(Class, ConstructorInfo, ...) - typed find for constructors
+       
//====================================================================================================
+
+       @Test
+       void f01_find_typedConstructor_returnsAnnotations() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var constructor = ci.getPublicConstructor(x -> 
x.getParameterCount() == 0).orElse(null);
+               assertNotNull(constructor);
+               
+               var annotations = provider.find(MultiTargetAnnotation.class, 
constructor, SELF);
+               
+               assertNotNull(annotations);
+               assertEquals(1, annotations.size());
+               assertEquals(2, annotations.get(0).getInt("value").orElse(0));
+       }
+
+       
//====================================================================================================
+       // find(ConstructorInfo, ...) - untyped find for constructors
+       
//====================================================================================================
+
+       @Test
+       void g01_find_untypedConstructor_returnsAllAnnotations() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var constructor = ci.getPublicConstructor(x -> 
x.getParameterCount() == 0).orElse(null);
+               assertNotNull(constructor);
+               
+               var annotations = provider.find(constructor, SELF);
+               
+               assertNotNull(annotations);
+               assertTrue(annotations.size() >= 1);
+               assertTrue(annotations.stream().anyMatch(a -> 
a.isType(MultiTargetAnnotation.class)));
+       }
+
+       
//====================================================================================================
+       // find(Class, MethodInfo, ...) - typed find for methods
+       
//====================================================================================================
+
+       @Test
+       void h01_find_typedMethod_returnsAnnotations() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               MethodInfo method = ci.getPublicMethod(x -> 
x.hasName("method1")).orElse(null);
+               assertNotNull(method);
+               
+               var annotations = provider.find(MultiTargetAnnotation.class, 
method, SELF);
+               
+               assertNotNull(annotations);
+               assertEquals(1, annotations.size());
+               assertEquals(3, annotations.get(0).getInt("value").orElse(0));
+       }
+
+       
//====================================================================================================
+       // find(MethodInfo, ...) - untyped find for methods
+       
//====================================================================================================
+
+       @Test
+       void i01_find_untypedMethod_returnsAllAnnotations() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var method = ci.getPublicMethod(x -> 
x.hasName("method1")).orElse(null);
+               assertNotNull(method);
+               
+               var annotations = provider.find(method, SELF);
+               
+               assertNotNull(annotations);
+               assertTrue(annotations.size() >= 1);
+               assertTrue(annotations.stream().anyMatch(a -> 
a.isType(MultiTargetAnnotation.class)));
+       }
+
+       
//====================================================================================================
+       // find(Class, ParameterInfo, ...) - typed find for parameters
+       
//====================================================================================================
+
+       @Test
+       void j01_find_typedParameter_returnsAnnotations() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var method = ci.getPublicMethod(x -> 
x.hasName("method2")).orElse(null);
+               assertNotNull(method);
+               ParameterInfo param = method.getParameter(0);
+               assertNotNull(param);
+               
+               var annotations = provider.find(MultiTargetAnnotation.class, 
param, SELF);
+               
+               assertNotNull(annotations);
+               assertEquals(1, annotations.size());
+               assertEquals(4, annotations.get(0).getInt("value").orElse(0));
+       }
+
+       
//====================================================================================================
+       // find(ParameterInfo, ...) - untyped find for parameters
+       
//====================================================================================================
+
+       @Test
+       void k01_find_untypedParameter_returnsAllAnnotations() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var method = ci.getPublicMethod(x -> 
x.hasName("method2")).orElse(null);
+               assertNotNull(method);
+               var param = method.getParameter(0);
+               assertNotNull(param);
+               
+               var annotations = provider.find(param, SELF);
+               
+               assertNotNull(annotations);
+               assertTrue(annotations.size() >= 1);
+               assertTrue(annotations.stream().anyMatch(a -> 
a.isType(MultiTargetAnnotation.class)));
+       }
+
+       
//====================================================================================================
+       // has(Class, ClassInfo, ...) - check existence for classes
+       
//====================================================================================================
+
+       @Test
+       void l01_has_typedClass_exists_returnsTrue() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               
+               assertTrue(provider.has(TestAnnotation.class, ci, SELF));
+       }
+
+       @Test
+       void l02_has_typedClass_notExists_returnsFalse() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               
+               assertFalse(provider.has(ParentAnnotation.class, ci, SELF));
+       }
+
+       
//====================================================================================================
+       // has(Class, ConstructorInfo, ...) - check existence for constructors
+       
//====================================================================================================
+
+       @Test
+       void l03_has_typedConstructor_exists_returnsTrue() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               ConstructorInfo constructor = ci.getPublicConstructor(x -> 
x.getParameterCount() == 0).orElse(null);
+               assertNotNull(constructor);
+               
+               assertTrue(provider.has(MultiTargetAnnotation.class, 
constructor, SELF));
+       }
+
+       @Test
+       void l04_has_typedConstructor_notExists_returnsFalse() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               ConstructorInfo constructor = ci.getPublicConstructor(x -> 
x.getParameterCount() == 0).orElse(null);
+               assertNotNull(constructor);
+               
+               assertFalse(provider.has(TestAnnotation.class, constructor, 
SELF));
+       }
+
+       
//====================================================================================================
+       // has(Class, FieldInfo, ...) - check existence for fields
+       
//====================================================================================================
+
+       @Test
+       void l05_has_typedField_exists_returnsTrue() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               FieldInfo field = ci.getPublicField(x -> 
x.hasName("field1")).orElse(null);
+               assertNotNull(field);
+               
+               assertTrue(provider.has(MultiTargetAnnotation.class, field, 
SELF));
+       }
+
+       @Test
+       void l06_has_typedField_notExists_returnsFalse() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               FieldInfo field = ci.getPublicField(x -> 
x.hasName("field1")).orElse(null);
+               assertNotNull(field);
+               
+               assertFalse(provider.has(TestAnnotation.class, field, SELF));
+       }
+
+       
//====================================================================================================
+       // has(Class, MethodInfo, ...) - check existence for methods
+       
//====================================================================================================
+
+       @Test
+       void l07_has_typedMethod_exists_returnsTrue() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               MethodInfo method = ci.getPublicMethod(x -> 
x.hasName("method1")).orElse(null);
+               assertNotNull(method);
+               
+               assertTrue(provider.has(MultiTargetAnnotation.class, method, 
SELF));
+       }
+
+       @Test
+       void l08_has_typedMethod_notExists_returnsFalse() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               MethodInfo method = ci.getPublicMethod(x -> 
x.hasName("method1")).orElse(null);
+               assertNotNull(method);
+               
+               assertFalse(provider.has(TestAnnotation.class, method, SELF));
+       }
+
+       
//====================================================================================================
+       // has(Class, ParameterInfo, ...) - check existence for parameters
+       
//====================================================================================================
+
+       @Test
+       void l09_has_typedParameter_exists_returnsTrue() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var method = ci.getPublicMethod(x -> 
x.hasName("method2")).orElse(null);
+               assertNotNull(method);
+               ParameterInfo param = method.getParameter(0);
+               assertNotNull(param);
+               
+               assertTrue(provider.has(MultiTargetAnnotation.class, param, 
SELF));
+       }
+
+       @Test
+       void l10_has_typedParameter_notExists_returnsFalse() {
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var method = ci.getPublicMethod(x -> 
x.hasName("method2")).orElse(null);
+               assertNotNull(method);
+               ParameterInfo param = method.getParameter(0);
+               assertNotNull(param);
+               
+               assertFalse(provider.has(TestAnnotation.class, param, SELF));
+       }
+
+       
//====================================================================================================
+       // Default traversal logic (lines 991-998) - when no traversals 
specified
+       
//====================================================================================================
+
+       @Test
+       void l11_find_typedClass_noTraversals_usesDefaultTraversals() {
+               // This test covers line 991-992 - default traversals for 
ClassInfo (PARENTS, PACKAGE)
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(ChildClass.class);
+               
+               // Call with no traversals - should use default (PARENTS, 
PACKAGE)
+               var annotations = provider.find(TestAnnotation.class, ci);
+               
+               assertNotNull(annotations);
+               // Should find annotation from child class
+               assertTrue(annotations.size() >= 1);
+       }
+
+       @Test
+       void l12_find_typedMethod_noTraversals_usesDefaultTraversals() {
+               // This test covers line 993-994 - default traversals for 
MethodInfo
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               MethodInfo method = ci.getPublicMethod(x -> 
x.hasName("method1")).orElse(null);
+               assertNotNull(method);
+               
+               // Call with no traversals - should use default (SELF, 
MATCHING_METHODS, DECLARING_CLASS, RETURN_TYPE, PACKAGE)
+               var annotations = provider.find(MultiTargetAnnotation.class, 
method);
+               
+               assertNotNull(annotations);
+               assertTrue(annotations.size() >= 1);
+       }
+
+       @Test
+       void l13_find_typedField_noTraversals_usesDefaultTraversals() {
+               // This test covers line 995-996 - default traversals for 
FieldInfo (SELF)
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               FieldInfo field = ci.getPublicField(x -> 
x.hasName("field1")).orElse(null);
+               assertNotNull(field);
+               
+               // Call with no traversals - should use default (SELF)
+               var annotations = provider.find(MultiTargetAnnotation.class, 
field);
+               
+               assertNotNull(annotations);
+               assertEquals(1, annotations.size());
+       }
+
+       @Test
+       void l14_find_typedConstructor_noTraversals_usesDefaultTraversals() {
+               // This test covers line 995-996 - default traversals for 
ConstructorInfo (SELF)
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               ConstructorInfo constructor = ci.getPublicConstructor(x -> 
x.getParameterCount() == 0).orElse(null);
+               assertNotNull(constructor);
+               
+               // Call with no traversals - should use default (SELF)
+               var annotations = provider.find(MultiTargetAnnotation.class, 
constructor);
+               
+               assertNotNull(annotations);
+               assertEquals(1, annotations.size());
+       }
+
+       @Test
+       void l15_find_typedParameter_noTraversals_usesDefaultTraversals() {
+               // This test covers line 997-998 - default traversals for 
ParameterInfo
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(TestClass.class);
+               var method = ci.getPublicMethod(x -> 
x.hasName("method2")).orElse(null);
+               assertNotNull(method);
+               ParameterInfo param = method.getParameter(0);
+               assertNotNull(param);
+               
+               // Call with no traversals - should use default (SELF, 
MATCHING_PARAMETERS, PARAMETER_TYPE)
+               var annotations = provider.find(MultiTargetAnnotation.class, 
param);
+               
+               assertNotNull(annotations);
+               assertTrue(annotations.size() >= 1);
+       }
+
+       @Test
+       void 
l16_find_typedClass_withPackageTraversal_nullPackage_handlesGracefully() {
+               // This test covers line 1014 - when getPackage() returns null
+               // Primitive types and arrays of primitives have no package
+               var provider = AnnotationProvider.create().build();
+               
+               // int.class has no package (getPackage() returns null)
+               var ci = ClassInfo.of(int.class);
+               assertNull(ci.getPackage(), "int.class should have no package");
+               
+               // Call with PACKAGE traversal - should handle null package 
gracefully
+               var annotations = provider.find(TestAnnotation.class, ci, 
AnnotationTraversal.PACKAGE);
+               
+               // Should not throw exception, just return empty list
+               assertNotNull(annotations);
+               assertEquals(0, annotations.size());
+       }
+
+       @Test
+       void 
l17_find_typedMethod_withMatchingMethodsTraversal_includesParentAndInterfaceMethods()
 {
+               // This test covers lines 1024-1025 - MATCHING_METHODS traversal
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(MatchingMethodChild.class);
+               MethodInfo method = ci.getPublicMethod(x -> 
x.hasName("matchingMethod")).orElse(null);
+               assertNotNull(method);
+               
+               // Verify the matching methods order: [child, interface, parent]
+               var matchingMethods = method.getMatchingMethods();
+               assertTrue(matchingMethods.size() >= 3, "Should have at least 
child, interface, and parent methods. Found: " + matchingMethods.size());
+               
+               // Verify we can find the parent method in the matching methods
+               var parentMethod = matchingMethods.stream()
+                       .filter(m -> 
MatchingMethodParent.class.equals(m.getDeclaringClass().inner()))
+                       .findFirst();
+               assertTrue(parentMethod.isPresent(), "Parent method should be 
in matching methods");
+               
+               // Verify the parent method has the annotation
+               var parentMethodAnnotations = 
parentMethod.get().getDeclaredAnnotations(MultiTargetAnnotation.class).toList();
+               assertTrue(parentMethodAnnotations.size() > 0, "Parent method 
should have annotation. Found " + parentMethodAnnotations.size() + " 
annotations");
+               var parentMethodAnnotation = parentMethodAnnotations.get(0);
+               // Try getInt first, then getValue as fallback
+               var parentAnnotationValue = 
parentMethodAnnotation.getInt("value").orElse(null);
+               if (parentAnnotationValue == null) {
+                       parentAnnotationValue = 
parentMethodAnnotation.getValue(Integer.class, "value").orElse(null);
+               }
+               assertEquals(20, parentAnnotationValue, "Parent method 
annotation should have value 20. Annotation: " + parentMethodAnnotation);
+               
+               // Skip the first (child method) - should have interface and 
parent
+               var methodsAfterSkip = 
matchingMethods.stream().skip(1).toList();
+               assertTrue(methodsAfterSkip.size() >= 2, "Should have interface 
and parent methods after skipping child");
+               
+               // Call with MATCHING_METHODS traversal - should include 
annotations from parent and interface methods
+               // Note: skip(1) skips the child method itself, so we get 
interface and parent
+               var annotations = provider.find(MultiTargetAnnotation.class, 
method, MATCHING_METHODS);
+               
+               assertNotNull(annotations);
+               // Should find annotations from:
+               // 1. Interface method (value=10) - from declared interfaces of 
child class
+               // 2. Parent class method (value=20) - from parent class
+               // Note: The child method itself is skipped by .skip(1)
+               assertTrue(annotations.size() >= 2, "Should find annotations 
from parent and interface matching methods. Found: " + annotations.size());
+               
+               // Debug: print what we found
+               var foundValues = annotations.stream()
+                       .map(a -> {
+                               Integer val = a.getInt("value").orElse(null);
+                               if (val == null) {
+                                       val = a.getValue(Integer.class, 
"value").orElse(null);
+                               }
+                               return val;
+                       })
+                       .filter(v -> v != null)
+                       .toList();
+               
+               // Verify we have the interface annotation (value=10) - comes 
first after skip(1)
+               var interfaceAnnotation = annotations.stream()
+                       .filter(a -> {
+                               Integer val = a.getInt("value").orElse(null);
+                               if (val == null) {
+                                       val = a.getValue(Integer.class, 
"value").orElse(null);
+                               }
+                               return val != null && val.intValue() == 10;
+                       })
+                       .findFirst();
+               assertTrue(interfaceAnnotation.isPresent(), "Should find 
annotation from interface method. Found values: " + foundValues);
+               
+               // Verify we have the parent annotation (value=20) - comes 
after interface
+               var parentAnnotation = annotations.stream()
+                       .filter(a -> {
+                               Integer val = a.getInt("value").orElse(null);
+                               if (val == null) {
+                                       val = a.getValue(Integer.class, 
"value").orElse(null);
+                               }
+                               return val != null && val.intValue() == 20;
+                       })
+                       .findFirst();
+               assertTrue(parentAnnotation.isPresent(), "Should find 
annotation from parent class method. Found values: " + foundValues + ", 
matching methods count: " + matchingMethods.size());
+       }
+
+       @Test
+       void 
l18_find_typedParameter_withMatchingParametersTraversal_includesParentAndInterfaceParameters()
 {
+               // This test covers line 1054 - MATCHING_PARAMETERS traversal
+               var provider = AnnotationProvider.create().build();
+               var ci = ClassInfo.of(MatchingParameterChild.class);
+               MethodInfo method = ci.getPublicMethod(x -> 
x.hasName("matchingParameterMethod")).orElse(null);
+               assertNotNull(method);
+               ParameterInfo param = method.getParameter(0);
+               assertNotNull(param);
+               
+               // Verify the matching parameters order: [child, interface, 
parent]
+               var matchingParameters = param.getMatchingParameters();
+               assertTrue(matchingParameters.size() >= 3, "Should have at 
least child, interface, and parent parameters. Found: " + 
matchingParameters.size());
+               
+               // Verify we can find the parent parameter in the matching 
parameters
+               var parentParameter = matchingParameters.stream()
+                       .filter(p -> {
+                               var paramMethod = p.getMethod();
+                               if (paramMethod != null) {
+                                       return 
MatchingParameterParent.class.equals(paramMethod.getDeclaringClass().inner());
+                               }
+                               return false;
+                       })
+                       .findFirst();
+               assertTrue(parentParameter.isPresent(), "Parent parameter 
should be in matching parameters");
+               
+               // Verify the parent parameter has the annotation
+               var parentParameterAnnotations = 
parentParameter.get().getAnnotations(MultiTargetAnnotation.class).toList();
+               assertTrue(parentParameterAnnotations.size() > 0, "Parent 
parameter should have annotation. Found " + parentParameterAnnotations.size() + 
" annotations");
+               var parentParameterAnnotation = 
parentParameterAnnotations.get(0);
+               var parentParameterValue = 
parentParameterAnnotation.getInt("value").orElse(null);
+               if (parentParameterValue == null) {
+                       parentParameterValue = 
parentParameterAnnotation.getValue(Integer.class, "value").orElse(null);
+               }
+               assertEquals(200, parentParameterValue, "Parent parameter 
annotation should have value 200. Annotation: " + parentParameterAnnotation);
+               
+               // Skip the first (child parameter) - should have interface and 
parent
+               var parametersAfterSkip = 
matchingParameters.stream().skip(1).toList();
+               assertTrue(parametersAfterSkip.size() >= 2, "Should have 
interface and parent parameters after skipping child");
+               
+               // Call with MATCHING_PARAMETERS traversal - should include 
annotations from parent and interface parameters
+               // Note: skip(1) skips the child parameter itself, so we get 
interface and parent
+               var annotations = provider.find(MultiTargetAnnotation.class, 
param, MATCHING_PARAMETERS);
+               
+               assertNotNull(annotations);
+               // Should find annotations from:
+               // 1. Interface parameter (value=100) - from declared 
interfaces of child class
+               // 2. Parent class parameter (value=200) - from parent class
+               // Note: The child parameter itself is skipped by .skip(1)
+               assertTrue(annotations.size() >= 2, "Should find annotations 
from parent and interface matching parameters. Found: " + annotations.size());
+               
+               // Debug: print what we found
+               var foundValues = annotations.stream()
+                       .map(a -> {
+                               Integer val = a.getInt("value").orElse(null);
+                               if (val == null) {
+                                       val = a.getValue(Integer.class, 
"value").orElse(null);
+                               }
+                               return val;
+                       })
+                       .filter(v -> v != null)
+                       .toList();
+               
+               // Verify we have the interface parameter annotation 
(value=100) - comes first after skip(1)
+               var interfaceAnnotation = annotations.stream()
+                       .filter(a -> {
+                               Integer val = a.getInt("value").orElse(null);
+                               if (val == null) {
+                                       val = a.getValue(Integer.class, 
"value").orElse(null);
+                               }
+                               return val != null && val.intValue() == 100;
+                       })
+                       .findFirst();
+               assertTrue(interfaceAnnotation.isPresent(), "Should find 
annotation from interface parameter. Found values: " + foundValues);
+               
+               // Verify we have the parent parameter annotation (value=200) - 
comes after interface
+               var parentAnnotation = annotations.stream()
+                       .filter(a -> {
+                               Integer val = a.getInt("value").orElse(null);
+                               if (val == null) {
+                                       val = a.getValue(Integer.class, 
"value").orElse(null);
+                               }
+                               return val != null && val.intValue() == 200;
+                       })
+                       .findFirst();
+               assertTrue(parentAnnotation.isPresent(), "Should find 
annotation from parent class parameter. Found values: " + foundValues + ", 
matching parameters count: " + matchingParameters.size());
+       }
+
+       @Test
+       void 
l19_find_typedMethod_withRuntimeAnnotation_loadsFromAnnotationMap() {
+               // This test covers line 1072 - load() method for Method objects
+               var runtimeAnnotation = new RuntimeOnAnnotation(new 
String[]{"org.apache.juneau.commons.reflect.AnnotationProvider_Test$TestClass.method1"},
 "runtimeMethod");
+               var provider = AnnotationProvider.create()
+                       .addRuntimeAnnotations(runtimeAnnotation)
+                       .build();
+               var ci = ClassInfo.of(TestClass.class);
+               MethodInfo method = ci.getPublicMethod(x -> 
x.hasName("method1")).orElse(null);
+               assertNotNull(method);
+               
+               // Call with SELF traversal - should trigger runtimeCache.get() 
which calls load() for Method
+               // This covers line 1072: annotationMap.find(mi.inner())
+               var annotations = provider.find(TestAnnotation.class, method, 
SELF);
+               
+               assertNotNull(annotations);
+               // Should find the runtime annotation
+               assertTrue(annotations.size() >= 1, "Should find at least the 
runtime annotation");
+               
+               // Verify runtime annotation is found
+               var runtimeAnnotationFound = annotations.stream()
+                       .filter(a -> 
a.getValue().orElse("").equals("runtimeMethod"))
+                       .findFirst();
+               assertTrue(runtimeAnnotationFound.isPresent(), "Should find 
runtime annotation on method");
+       }
+
+       @Test
+       void 
l20_find_typedConstructor_withRuntimeAnnotation_loadsFromAnnotationMap() {
+               // This test covers line 1080 - load() method for Constructor 
objects
+               // Constructor format for ReflectionMap is "ClassName()" for 
no-arg constructor
+               var runtimeAnnotation = new RuntimeOnAnnotation(new 
String[]{"org.apache.juneau.commons.reflect.AnnotationProvider_Test$TestClass()"},
 "runtimeConstructor");
+               var provider = AnnotationProvider.create()
+                       .addRuntimeAnnotations(runtimeAnnotation)
+                       .build();
+               var ci = ClassInfo.of(TestClass.class);
+               ConstructorInfo constructor = ci.getPublicConstructor(x -> 
x.getParameterCount() == 0).orElse(null);
+               assertNotNull(constructor);
+               
+               // Call with SELF traversal - should trigger runtimeCache.get() 
which calls load() for Constructor
+               // This covers line 1080: annotationMap.find(ci.inner())
+               var annotations = provider.find(TestAnnotation.class, 
constructor, SELF);
+               
+               assertNotNull(annotations);
+               // Should find the runtime annotation
+               assertTrue(annotations.size() >= 1, "Should find at least the 
runtime annotation");
+               
+               // Verify runtime annotation is found
+               var runtimeAnnotationFound = annotations.stream()
+                       .filter(a -> 
a.getValue().orElse("").equals("runtimeConstructor"))
+                       .findFirst();
+               assertTrue(runtimeAnnotationFound.isPresent(), "Should find 
runtime annotation on constructor");
+       }
+
+       
//====================================================================================================
+       // Builder methods
+       
//====================================================================================================
+
+       @Test
+       void m01_builder_cacheMode_buildsProvider() {
+               var provider = AnnotationProvider.create()
+                       .cacheMode(CacheMode.NONE)
+                       .build();
+               assertNotNull(provider);
+       }
+
+       @Test
+       void m02_builder_logOnExit_buildsProvider() {
+               var provider = AnnotationProvider.create()
+                       .logOnExit()
+                       .build();
+               assertNotNull(provider);
+       }
+
+       @Test
+       void m03_builder_logOnExit_withBoolean_coversLine402() {
+               // This test covers line 402 - logOnExit(boolean value)
+               var provider1 = AnnotationProvider.create()
+                       .logOnExit(true)
+                       .build();
+               assertNotNull(provider1);
+               
+               var provider2 = AnnotationProvider.create()
+                       .logOnExit(false)
+                       .build();
+               assertNotNull(provider2);
+       }
+
+       @Test
+       void m04_builder_chaining_buildsProvider() {
+               var provider = AnnotationProvider.create()
+                       .cacheMode(CacheMode.NONE)
+                       .logOnExit()
+                       .build();
+               assertNotNull(provider);
+       }
+
+       
//====================================================================================================
+       // addRuntimeAnnotations(Annotation...) - varargs version
+       
//====================================================================================================
+
+       // Simple runtime annotation implementation for testing with onClass()
+       private static class RuntimeTestAnnotation implements TestAnnotation {
+               private final Class<?>[] onClass;
+               private final String value;
+
+               RuntimeTestAnnotation(Class<?>[] onClass, String value) {
+                       this.onClass = onClass;
+                       this.value = value;
+               }
+
+               @Override
+               public String value() {
+                       return value;
+               }
+
+               @Override
+               public Class<? extends Annotation> annotationType() {
+                       return TestAnnotation.class;
+               }
+
+               public Class<?>[] onClass() {
+                       return onClass;
+               }
+
+               @Override
+               public boolean equals(Object obj) {
+                       if (!(obj instanceof TestAnnotation))
+                               return false;
+                       TestAnnotation other = (TestAnnotation)obj;
+                       return value.equals(other.value());
+               }
+
+               @Override
+               public int hashCode() {
+                       return value.hashCode();
+               }
+
+               @Override
+               public String toString() {
+                       return "@TestAnnotation(value=" + value + ")";
+               }
+       }
+
+       // Runtime annotation implementation with on() method (String[] 
targeting)
+       private static class RuntimeOnAnnotation implements TestAnnotation {
+               private final String[] on;
+               private final String value;
+
+               RuntimeOnAnnotation(String[] on, String value) {
+                       this.on = on;
+                       this.value = value;
+               }
+
+               @Override
+               public String value() {
+                       return value;
+               }
+
+               @Override
+               public Class<? extends Annotation> annotationType() {
+                       return TestAnnotation.class;
+               }
+
+               public String[] on() {
+                       return on;
+               }
+
+               @Override
+               public boolean equals(Object obj) {
+                       if (!(obj instanceof TestAnnotation))
+                               return false;
+                       TestAnnotation other = (TestAnnotation)obj;
+                       return value.equals(other.value());
+               }
+
+               @Override
+               public int hashCode() {
+                       return value.hashCode();
+               }
+
+               @Override
+               public String toString() {
+                       return "@TestAnnotation(value=" + value + ")";
+               }
+       }
+
+       @Test
+       void n01_addRuntimeAnnotations_varargs_callsListVersion() {
+               // This test covers line 245 - the varargs version that 
converts to list
+               var runtimeAnnotation1 = new RuntimeTestAnnotation(new 
Class<?>[]{TestClass.class}, "runtime1");
+               var runtimeAnnotation2 = new RuntimeTestAnnotation(new 
Class<?>[]{ParentClass.class}, "runtime2");
+               
+               var provider = AnnotationProvider.create()
+                       .addRuntimeAnnotations(runtimeAnnotation1, 
runtimeAnnotation2)  // Varargs - covers line 245
+                       .build();
+               
+               assertNotNull(provider);
+               
+               // Verify the runtime annotations are applied
+               var ci = ClassInfo.of(TestClass.class);
+               var annotations = provider.find(TestAnnotation.class, ci, SELF);
+               
+               // Should find the runtime annotation
+               assertTrue(annotations.size() >= 1);
+               var runtimeAnnotation = annotations.stream()
+                       .filter(a -> a.getValue().orElse("").equals("runtime1"))
+                       .findFirst();
+               assertTrue(runtimeAnnotation.isPresent());
+       }
+
+       @Test
+       void n04_addRuntimeAnnotations_withOnMethod_coversLine343() {
+               // This test covers line 343 - the on() method returning 
String[] is processed
+               var className = TestClass.class.getName();
+               var runtimeAnnotation = new RuntimeOnAnnotation(new 
String[]{className}, "runtimeOn");
+               
+               var provider = AnnotationProvider.create()
+                       .addRuntimeAnnotations(runtimeAnnotation)
+                       .build();
+               
+               assertNotNull(provider);
+               
+               // Verify the runtime annotation is applied using on() method
+               var ci = ClassInfo.of(TestClass.class);
+               var annotations = provider.find(TestAnnotation.class, ci, SELF);
+               
+               // Should find the runtime annotation
+               assertTrue(annotations.size() >= 1);
+               var runtimeAnnotationFound = annotations.stream()
+                       .filter(a -> 
a.getValue().orElse("").equals("runtimeOn"))
+                       .findFirst();
+               assertTrue(runtimeAnnotationFound.isPresent());
+       }
+
+       // Runtime annotation with invalid onClass() return type (not Class[])
+       private static class InvalidOnClassAnnotation implements TestAnnotation 
{
+               @Override
+               public String value() {
+                       return "invalid";
+               }
+
+               @Override
+               public Class<? extends Annotation> annotationType() {
+                       return TestAnnotation.class;
+               }
+
+               public String onClass() {  // Wrong return type - should be 
Class[]
+                       return "invalid";
+               }
+
+               @Override
+               public boolean equals(Object obj) {
+                       return obj instanceof TestAnnotation;
+               }
+
+               @Override
+               public int hashCode() {
+                       return 0;
+               }
+
+               @Override
+               public String toString() {
+                       return "@TestAnnotation";
+               }
+       }
+
+       // Runtime annotation with invalid on() return type (not String[])
+       private static class InvalidOnAnnotation implements TestAnnotation {
+               @Override
+               public String value() {
+                       return "invalid";
+               }
+
+               @Override
+               public Class<? extends Annotation> annotationType() {
+                       return TestAnnotation.class;
+               }
+
+               public String on() {  // Wrong return type - should be String[]
+                       return "invalid";
+               }
+
+               @Override
+               public boolean equals(Object obj) {
+                       return obj instanceof TestAnnotation;
+               }
+
+               @Override
+               public int hashCode() {
+                       return 0;
+               }
+
+               @Override
+               public String toString() {
+                       return "@TestAnnotation";
+               }
+       }
+
+       @Test
+       void 
n02_addRuntimeAnnotations_invalidOnClassReturnType_throwsException() {
+               // This test covers line 334 - onClass() method with wrong 
return type
+               var invalidAnnotation = new InvalidOnClassAnnotation();
+               
+               assertThrows(BeanRuntimeException.class, () -> {
+                       AnnotationProvider.create()
+                               .addRuntimeAnnotations(invalidAnnotation)
+                               .build();
+               });
+       }
+
+       @Test
+       void n03_addRuntimeAnnotations_invalidOnReturnType_throwsException() {
+               // This test covers line 341 - on() method with wrong return 
type
+               var invalidAnnotation = new InvalidOnAnnotation();
+               
+               assertThrows(BeanRuntimeException.class, () -> {
+                       AnnotationProvider.create()
+                               .addRuntimeAnnotations(invalidAnnotation)
+                               .build();
+               });
+       }
+
+       // Runtime annotation that throws an exception when onClass() is invoked
+       private static class ThrowingOnClassAnnotation implements 
TestAnnotation {
+               @Override
+               public String value() {
+                       return "throwing";
+               }
+
+               @Override
+               public Class<? extends Annotation> annotationType() {
+                       return TestAnnotation.class;
+               }
+
+               public Class<?>[] onClass() {
+                       throw new RuntimeException("Test exception from 
onClass()");
+               }
+
+               @Override
+               public boolean equals(Object obj) {
+                       return obj instanceof TestAnnotation;
+               }
+
+               @Override
+               public int hashCode() {
+                       return 0;
+               }
+
+               @Override
+               public String toString() {
+                       return "@TestAnnotation";
+               }
+       }
+
+       @Test
+       void n05_addRuntimeAnnotations_throwingOnClass_coversLine349() {
+               // This test covers line 349 - exception during method 
invocation (not BeanRuntimeException)
+               var throwingAnnotation = new ThrowingOnClassAnnotation();
+               
+               // The exception from onClass() will be caught and wrapped in 
BeanRuntimeException
+               assertThrows(BeanRuntimeException.class, () -> {
+                       AnnotationProvider.create()
+                               .addRuntimeAnnotations(throwingAnnotation)
+                               .build();
+               });
+       }
+}
+

Reply via email to