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 df6d2e89ec Unit tests
df6d2e89ec is described below
commit df6d2e89eccf4b20805931dcb00505844ba77238
Author: James Bognar <[email protected]>
AuthorDate: Wed Dec 3 09:00:49 2025 -0800
Unit tests
---
AI.md | 1 +
.../juneau/commons/reflect/AnnotationProvider.java | 2 +-
.../apache/juneau/commons/reflect/ClassInfo.java | 47 +-
.../juneau/commons/reflect/ConstructorInfo.java | 2 +-
.../juneau/commons/reflect/ExecutableInfo.java | 39 +-
.../apache/juneau/commons/reflect/FieldInfo.java | 14 +-
.../apache/juneau/commons/reflect/MethodInfo.java | 11 -
.../juneau/commons/reflect/ParameterInfo.java | 10 -
.../src/test/java/DefaultPackageTestClass.java | 2 +
.../juneau/commons/reflect/ClassInfo_Test.java | 479 ++++++++++++++++++++-
.../juneau/commons/reflect/ElementInfo_Test.java | 16 +
.../commons/reflect/ExecutableInfo_Test.java | 37 +-
.../juneau/commons/reflect/FieldInfo_Test.java | 26 +-
13 files changed, 583 insertions(+), 103 deletions(-)
diff --git a/AI.md b/AI.md
index f28083f484..44e562af62 100644
--- a/AI.md
+++ b/AI.md
@@ -350,6 +350,7 @@ A reusable Python script is available at
`scripts/build-and-test.py` for common
- Use JaCoCo reports to identify missing coverage
- Focus on methods with 0% coverage first
- Add comprehensive tests for all code paths
+- **Hard-to-Test Code Marking**: When a line of code is found to not be fully
testable (e.g., requires complex setup, compiler-generated code, or unreachable
branches), add a comment `// HTT` (Hard To Test) on that line to document why
it's difficult to test
### 8. File Organization and Naming
- Follow established file naming conventions
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 2c019dd8c3..261f1bd083 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
@@ -358,7 +358,7 @@ public class AnnotationProvider {
* @return A new immutable {@link AnnotationProvider} instance.
*/
public AnnotationProvider build() {
- if ((runtimeAnnotations == null ||
runtimeAnnotations.isEmpty()) && INSTANCE != null)
+ if (isEmpty(runtimeAnnotations) && INSTANCE != null)
return INSTANCE;
return new AnnotationProvider(this);
}
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ClassInfo.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ClassInfo.java
index 7cfa631cb2..35916f7050 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ClassInfo.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ClassInfo.java
@@ -387,8 +387,7 @@ public class ClassInfo extends ElementInfo implements
Annotatable {
public ClassInfo arrayType() {
if (inner == null)
return null;
- var at = inner.arrayType();
- return at == null ? null : of(at);
+ return of(inner.arrayType());
}
/**
@@ -432,9 +431,8 @@ public class ClassInfo extends ElementInfo implements
Annotatable {
return ! isPrimitive(); // Primitives can't accept
null, all other types can
if (inner.isInstance(child))
return true;
- if (this.isPrimitive() || child.getClass().isPrimitive()) {
+ if (this.isPrimitive())
return
this.getWrapperIfPrimitive().isParentOf(of(child).getWrapperIfPrimitive());
- }
return false;
}
@@ -1255,8 +1253,7 @@ public class ClassInfo extends ElementInfo implements
Annotatable {
public ClassInfo getNestHost() {
if (inner == null)
return null;
- var nh = inner.getNestHost();
- return nh == null ? null : of(nh);
+ return of(inner.getNestHost());
}
/**
@@ -1304,7 +1301,18 @@ public class ClassInfo extends ElementInfo implements
Annotatable {
/**
* Returns the package of this class.
*
- * @return The package of this class wrapped in a {@link PackageInfo},
or <jk>null</jk> if this class has no package.
+ * <p>
+ * Returns <jk>null</jk> in the following cases:
+ * <ul>
+ * <li>Primitive types (e.g., <c>int.class</c>,
<c>boolean.class</c>)
+ * <li>Array types of primitives (e.g., <c>int[].class</c>)
+ * <li>Classes in the default (unnamed) package - while the
default package is technically a package,
+ * Java's <c>Class.getPackage()</c> returns <jk>null</jk>
for classes in the default package because
+ * it has no name or associated <c>Package</c> object
+ * </ul>
+ *
+ * @return The package of this class wrapped in a {@link PackageInfo},
or <jk>null</jk> if this class has no package
+ * or is in the default (unnamed) package.
*/
public PackageInfo getPackage() { return packageInfo.get(); }
@@ -1364,11 +1372,14 @@ public class ClassInfo extends ElementInfo implements
Annotatable {
} else if (actualType instanceof TypeVariable<?> actualType3) {
var nestedOuterTypes = new LinkedList<Class<?>>();
for (Class<?> ec = cc.getEnclosingClass(); nn(ec); ec =
ec.getEnclosingClass()) {
- var outerClass = cc.getClass();
+ var outerClass = ec;
nestedOuterTypes.add(outerClass);
var outerTypeMap = new HashMap<Type,Type>();
extractTypes(outerTypeMap, outerClass);
for (var entry : outerTypeMap.entrySet()) {
+ // HTT - Hard to test - requires nested
generics where a type variable in an inner class
+ // maps to another TypeVariable (not a
Class) in the outer class's type map, which then
+ // needs to be resolved further up the
enclosing class chain.
var key = entry.getKey();
var value = entry.getValue();
if (key instanceof TypeVariable<?>
key2) {
@@ -1793,16 +1804,11 @@ public class ClassInfo extends ElementInfo implements
Annotatable {
case SEALED -> isSealed();
case NOT_SEALED -> ! isSealed();
case SYNTHETIC -> isSynthetic();
- case NOT_SYNTHETIC -> ! isSynthetic();
+ case NOT_SYNTHETIC -> ! isSynthetic(); // HTT -
synthetic classes are compiler-generated and difficult to create in tests
default -> super.is(flag);
};
}
- @Override
- public boolean isAll(ElementFlag...flags) {
- return stream(flags).allMatch(this::is);
- }
-
/**
* Returns <jk>true</jk> if this class is an annotation.
*
@@ -1833,11 +1839,6 @@ public class ClassInfo extends ElementInfo implements
Annotatable {
return false;
}
- @Override
- public boolean isAny(ElementFlag...flags) {
- return stream(flags).anyMatch(this::is);
- }
-
/**
* Returns <jk>true</jk> if this class is an array.
*
@@ -2084,6 +2085,9 @@ public class ClassInfo extends ElementInfo implements
Annotatable {
return false;
if (inner.isAssignableFrom(child.inner()))
return true;
+ // If at least one is a primitive, convert both to wrappers and
compare
+ // Note: If both are primitives and the same type, we would
have returned true above
+ // So this branch is only reached when at least one is a
primitive and they're not directly assignable
if (this.isPrimitive() || child.isPrimitive()) {
return
this.getWrapperIfPrimitive().isParentOf(child.getWrapperIfPrimitive());
}
@@ -2166,7 +2170,7 @@ public class ClassInfo extends ElementInfo implements
Annotatable {
*
* @return <jk>true</jk> if this class is synthetic.
*/
- public boolean isSynthetic() { return nn(inner) && inner.isSynthetic();
}
+ public boolean isSynthetic() { return nn(inner) && inner.isSynthetic();
} // HTT
/**
* Identifies if the specified visibility matches this constructor.
@@ -2357,8 +2361,7 @@ public class ClassInfo extends ElementInfo implements
Annotatable {
.filter(m -> {
var rct = m.getReturnType().getComponentType();
if (rct.hasAnnotation(Repeatable.class)) {
- var r =
rct.getAnnotations(Repeatable.class).findFirst().map(AnnotationInfo::inner).orElse(null);
- return r != null &&
r.value().equals(inner);
+ return
rct.getAnnotations(Repeatable.class).findFirst().map(AnnotationInfo::inner).orElse(null).value().equals(inner);
}
return false;
})
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ConstructorInfo.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ConstructorInfo.java
index 3edfa135b4..8f8a81555e 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ConstructorInfo.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ConstructorInfo.java
@@ -191,7 +191,7 @@ public class ConstructorInfo extends ExecutableInfo
implements Comparable<Constr
} catch (InvocationTargetException e) {
throw new
ExecutableException(e.getTargetException());
}
- }, e -> new ExecutableException(e));
+ }, e -> new ExecutableException(e)); // HTT
}
/**
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ExecutableInfo.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ExecutableInfo.java
index e36204fa39..653a21462d 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ExecutableInfo.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ExecutableInfo.java
@@ -528,23 +528,13 @@ public abstract class ExecutableInfo extends
AccessibleInfo {
case HAS_PARAMS -> hasParameters();
case HAS_NO_PARAMS -> getParameterCount() == 0;
case SYNTHETIC -> isSynthetic();
- case NOT_SYNTHETIC -> ! isSynthetic();
+ case NOT_SYNTHETIC -> ! isSynthetic(); // HTT
case VARARGS -> isVarArgs();
case NOT_VARARGS -> ! isVarArgs();
default -> super.is(flag);
};
}
- @Override
- public boolean isAll(ElementFlag...flags) {
- return stream(flags).allMatch(this::is);
- }
-
- @Override
- public boolean isAny(ElementFlag...flags) {
- return stream(flags).anyMatch(this::is);
- }
-
/**
* Returns <jk>true</jk> if this executable represents a {@link
Constructor}.
*
@@ -772,25 +762,18 @@ public abstract class ExecutableInfo extends
AccessibleInfo {
private List<ParameterInfo> findParameters() {
var rp = inner.getParameters();
- var ptc = inner.getParameterTypes();
- // Note that due to a bug involving Enum constructors,
getGenericParameterTypes() may
- // always return an empty array. This appears to be fixed in
Java 8 b75.
- var ptt = inner.getGenericParameterTypes();
+ var ptc = inner.getParameterTypes(); // Class<?>[] - includes
all parameters (including synthetic enclosing instance)
+ var ptt = inner.getGenericParameterTypes(); // Type[] -
generic type information
Type[] genericTypes;
- if (ptt.length != ptc.length) {
- // Bug in javac: generic type array excludes enclosing
instance parameter
- // for inner classes with at least one generic
constructor parameter.
- if (ptt.length + 1 == ptc.length) {
- var ptt2 = new Type[ptc.length];
- ptt2[0] = ptc[0];
- for (var i = 0; i < ptt.length; i++)
- ptt2[i + 1] = ptt[i];
- genericTypes = ptt2;
- } else {
- genericTypes = ptc;
- }
- } else {
+
+ if (ptt.length == ptc.length) {
genericTypes = ptt;
+ } else {
+ var ptt2 = new Type[ptc.length];
+ ptt2[0] = ptc[0]; // Enclosing instance type
(Class<?>, not a generic Type)
+ for (var i = 0; i < ptt.length; i++)
+ ptt2[i + 1] = ptt[i]; // Copy remaining
generic types
+ genericTypes = ptt2;
}
return IntStream.range(0, rp.length).mapToObj(i -> new
ParameterInfo(this, rp[i], i, ClassInfo.of(ptc[i], genericTypes[i]))).toList();
}
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/FieldInfo.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/FieldInfo.java
index 7c375fe85b..a8c25dada6 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/FieldInfo.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/FieldInfo.java
@@ -312,21 +312,11 @@ public class FieldInfo extends AccessibleInfo implements
Comparable<FieldInfo>,
case ENUM_CONSTANT -> isEnumConstant();
case NOT_ENUM_CONSTANT -> ! isEnumConstant();
case SYNTHETIC -> isSynthetic();
- case NOT_SYNTHETIC -> ! isSynthetic();
+ case NOT_SYNTHETIC -> ! isSynthetic(); // HTT
default -> super.is(flag);
};
}
- @Override
- public boolean isAll(ElementFlag...flags) {
- return stream(flags).allMatch(this::is);
- }
-
- @Override
- public boolean isAny(ElementFlag...flags) {
- return stream(flags).anyMatch(this::is);
- }
-
/**
* Returns <jk>true</jk> if this field has the {@link Deprecated
@Deprecated} annotation on it.
*
@@ -458,6 +448,8 @@ public class FieldInfo extends AccessibleInfo implements
Comparable<FieldInfo>,
var pi = dc.getPackage();
if (nn(pi))
sb.append(pi.getName()).append('.');
+ // HTT - false branch (pi == null) is hard to test: some
classloaders return Package objects
+ // even for default package classes, though Java API spec says
it should return null
dc.appendNameFormatted(sb, SHORT, true, '$', BRACKETS);
sb.append('.').append(getName());
return sb.toString();
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/MethodInfo.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/MethodInfo.java
index c58978958b..3e114cf49a 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/MethodInfo.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/MethodInfo.java
@@ -17,7 +17,6 @@
package org.apache.juneau.commons.reflect;
import static org.apache.juneau.commons.utils.AssertionUtils.*;
-import static org.apache.juneau.commons.utils.CollectionUtils.*;
import static org.apache.juneau.commons.utils.Utils.*;
import java.beans.*;
@@ -602,20 +601,10 @@ public class MethodInfo extends ExecutableInfo implements
Comparable<MethodInfo>
};
}
- @Override
- public boolean isAll(ElementFlag...flags) {
- return stream(flags).allMatch(this::is);
- }
-
//-----------------------------------------------------------------------------------------------------------------
// High Priority Methods (direct Method API compatibility)
//-----------------------------------------------------------------------------------------------------------------
- @Override
- public boolean isAny(ElementFlag...flags) {
- return stream(flags).anyMatch(this::is);
- }
-
/**
* Returns <jk>true</jk> if this method is a bridge method.
*
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ParameterInfo.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ParameterInfo.java
index f4e554bcb7..e6853005af 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ParameterInfo.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ParameterInfo.java
@@ -475,16 +475,6 @@ public class ParameterInfo extends ElementInfo implements
Annotatable {
};
}
- @Override
- public boolean isAll(ElementFlag...flags) {
- return stream(flags).allMatch(this::is);
- }
-
- @Override
- public boolean isAny(ElementFlag...flags) {
- return stream(flags).anyMatch(this::is);
- }
-
/**
* Returns <jk>true</jk> if this parameter is implicitly declared in
source code.
*
diff --git a/juneau-utest/src/test/java/DefaultPackageTestClass.java
b/juneau-utest/src/test/java/DefaultPackageTestClass.java
index e4ef971c8d..1972c24a5a 100644
--- a/juneau-utest/src/test/java/DefaultPackageTestClass.java
+++ b/juneau-utest/src/test/java/DefaultPackageTestClass.java
@@ -19,6 +19,8 @@
// where a class has $ (is an inner class) but has no package
public class DefaultPackageTestClass {
+ public int testField; // Field for testing FieldInfo.getFullName()
with null package
+
public static class InnerClass {
public static class NestedInner {}
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ClassInfo_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ClassInfo_Test.java
index 81bd3afad8..3b8f1e267a 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ClassInfo_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ClassInfo_Test.java
@@ -49,6 +49,50 @@ public class ClassInfo_Test extends TestBase {
int value();
}
+ // Test annotations for repeatable annotation testing
+ @Repeatable(TestRepeatableContainer.class)
+ @Retention(RUNTIME)
+ @Target({TYPE, METHOD, FIELD})
+ public static @interface TestRepeatable {
+ String value();
+ }
+
+ @Retention(RUNTIME)
+ @Target({TYPE, METHOD, FIELD})
+ public static @interface TestRepeatableContainer {
+ TestRepeatable[] value(); // This makes
TestRepeatableContainer the container for TestRepeatable
+ }
+
+ // Another repeatable annotation with a different container
+ @Repeatable(OtherRepeatableContainer.class)
+ @Retention(RUNTIME)
+ @Target({TYPE, METHOD, FIELD})
+ public static @interface OtherRepeatable {
+ String value();
+ }
+
+ @Retention(RUNTIME)
+ @Target({TYPE, METHOD, FIELD})
+ public static @interface OtherRepeatableContainer {
+ OtherRepeatable[] value();
+ }
+
+ // An annotation with a value() method returning an array, but the
component type is not repeatable
+ @Retention(RUNTIME)
+ @Target({TYPE, METHOD, FIELD})
+ public static @interface NonRepeatableArrayContainer {
+ String[] value(); // Component type is String, not a
repeatable annotation
+ }
+
+ // An annotation with a value() method returning an array of a
repeatable annotation,
+ // but the @Repeatable annotation points to a different container (not
this class)
+ // This tests the case where r != null is true but
r.value().equals(inner) is false
+ @Retention(RUNTIME)
+ @Target({TYPE, METHOD, FIELD})
+ public static @interface WrongContainer {
+ TestRepeatable[] value(); // Returns TestRepeatable[], but
TestRepeatable's @Repeatable points to TestRepeatableContainer, not
WrongContainer
+ }
+
@Documented
@Target(TYPE)
@Retention(RUNTIME)
@@ -504,15 +548,25 @@ public class ClassInfo_Test extends TestBase {
assertNotNull(arrayType2);
assertEquals(String[][].class, arrayType2.inner());
- // For types without inner class, should return null
+ // For types without inner class, should return null (line
388-389)
var ci3 = ClassInfo.of((Class<?>)null, pType);
assertNull(ci3.arrayType());
- // Test when inner.arrayType() returns null (line 391)
- // Note: Most classes' arrayType() returns a valid array class
- // The null check at line 391 handles the case when
inner.arrayType() returns null
- // This is a defensive check, but in practice most classes have
valid array types
- // The line is covered by the null inner check above
+ // Test line 391: when inner.arrayType() returns null
+ // In Java, Class.arrayType() should always return a non-null
value for valid classes
+ // However, the code has a defensive check at line 391: return
at == null ? null : of(at);
+ // To test the at == null branch, we need inner != null but
inner.arrayType() == null
+ // This is difficult to achieve with normal Java classes, but
we can verify the non-null branch works
+ // The non-null branch (at != null) is already covered above
with String.class and String[].class
+
+ // Verify that the non-null branch works correctly (at != null
-> return of(at))
+ var ci4 = ClassInfo.of(Integer.class);
+ var arrayType3 = ci4.arrayType();
+ assertNotNull(arrayType3);
+ assertEquals(Integer[].class, arrayType3.inner());
+
+ // Note: The at == null branch at line 391 is a defensive check
that may be unreachable
+ // in practice with normal Java classes, but the code includes
it for safety
}
//====================================================================================================
@@ -702,6 +756,22 @@ public class ClassInfo_Test extends TestBase {
// Number can accept int (parent type)
assertTrue(ClassInfo.of(Number.class).canAcceptArg(42));
assertTrue(ClassInfo.of(Number.class).canAcceptArg(Integer.valueOf(42)));
+
+ // Test line 434: all 4 branches of if (this.isPrimitive() ||
child.getClass().isPrimitive())
+ // Branch 1: this.isPrimitive() == true &&
child.getClass().isPrimitive() == true
+ // Note: When you pass a primitive value like 42, it gets
autoboxed to Integer, so child.getClass() returns Integer.class (not primitive)
+ // This branch is likely unreachable in practice since
primitive values are always autoboxed
+
+ // Branch 2: this.isPrimitive() == true &&
child.getClass().isPrimitive() == false (already covered above at line 707)
+ // Branch 3: this.isPrimitive() == false &&
child.getClass().isPrimitive() == true (already covered above at line 710)
+ // Branch 4: this.isPrimitive() == false &&
child.getClass().isPrimitive() == false
+ // This happens when both are non-primitive, isInstance returns
false, and we fall through to return false
+ // This is the missing branch - need to test when condition is
false (both false), so we skip the if and return false
+ assertFalse(ClassInfo.of(String.class).canAcceptArg(new
Object())); // Both non-primitive, isInstance false, condition false, returns
false
+ assertFalse(ClassInfo.of(String.class).canAcceptArg(42)); //
String is not primitive, Integer is not primitive, isInstance false, condition
false, returns false
+
+ // Test case where this is primitive but child is not (branch
2) - different primitive types that don't match
+
assertFalse(ClassInfo.of(int.class).canAcceptArg(Long.valueOf(42L))); // int is
primitive, Long is not primitive, different types, condition true, returns
false from isParentOf
}
//====================================================================================================
@@ -746,9 +816,23 @@ public class ClassInfo_Test extends TestBase {
assertEquals(pTypeGenericInfo,
pTypeGenericInfo.getComponentType());
assertEquals(pTypeGenericArgInfo,
pTypeGenericArgInfo.getComponentType());
- // For types without inner class, should return null
+ // For types without inner class, should return null (line
474-475)
var ci = ClassInfo.of((Class<?>)null, pType);
assertNull(ci.componentType());
+
+ // Test line 476-477: componentType() method
+ // For non-array types, inner.componentType() returns null, so
ct == null branch is taken
+ var nonArray = ClassInfo.of(String.class);
+ var ct = nonArray.componentType();
+ // String.class.componentType() returns null for non-array types
+ // So the ternary at line 477: ct == null ? null : of(ct) takes
the null branch
+ assertNull(ct);
+
+ // For array types, inner.componentType() returns non-null, so
ct != null branch is taken
+ var array = ClassInfo.of(String[].class);
+ var ct2 = array.componentType();
+ assertNotNull(ct2);
+ assertEquals(String.class, ct2.inner());
}
//====================================================================================================
@@ -804,6 +888,12 @@ public class ClassInfo_Test extends TestBase {
// G4 has no declared annotations (inherits from G3)
var declared2 = g4.getDeclaredAnnotations();
assertTrue(declared2.isEmpty());
+
+ // Test with null inner (line 231: opt(inner) returns empty
when inner is null)
+ var ci = ClassInfo.of((Class<?>)null, pType);
+ var declared3 = ci.getDeclaredAnnotations();
+ assertNotNull(declared3);
+ assertTrue(declared3.isEmpty()); // Should return empty list
when inner is null
}
//====================================================================================================
@@ -996,16 +1086,27 @@ public class ClassInfo_Test extends TestBase {
assertNull(enclosing2);
}
+ // Helper class for testing getEnclosingConstructor with non-null result
+ static class EnclosingConstructorTestClass {
+ EnclosingConstructorTestClass() {
+ // Local class inside constructor
+ class LocalInConstructor {}
+ // Store the class for testing
+ ClassInfo_Test.localInConstructorClass =
LocalInConstructor.class;
+ }
+ }
+ static Class<?> localInConstructorClass;
+
//====================================================================================================
// getEnclosingConstructor()
//====================================================================================================
@Test
void a025_getEnclosingConstructor() {
- // For types with null inner, should return null
+ // For types with null inner, should return null (line 949-950)
var ci = ClassInfo.of((Class<?>)null, pType);
assertNull(ci.getEnclosingConstructor());
- // For classes not declared in constructor, should return null
(line 954)
+ // For classes not declared in constructor, should return null
(line 952: ec == null branch)
// Regular classes don't have enclosing constructors
assertNull(aClass.getEnclosingConstructor());
assertNull(ClassInfo.of(String.class).getEnclosingConstructor());
@@ -1014,6 +1115,18 @@ public class ClassInfo_Test extends TestBase {
var local = ClassInfo.of(LocalClass.class);
var constructor = local.getEnclosingConstructor();
assertNull(constructor);
+
+ // Test line 952: ec != null branch - class declared inside
constructor
+ // Create an instance to trigger the constructor and capture
the local class
+ new EnclosingConstructorTestClass();
+ if (localInConstructorClass != null) {
+ var localInCtor = ClassInfo.of(localInConstructorClass);
+ var enclosingCtor =
localInCtor.getEnclosingConstructor();
+ // Verify that getEnclosingConstructor() returns
non-null (covers line 952: ec != null branch)
+ // This proves that inner.getEnclosingConstructor()
returned non-null, so the ternary
+ // operator on line 952 took the non-null branch and
called getConstructor(ec)
+ assertNotNull(enclosingCtor, "Expected non-null
enclosing constructor for local class in constructor");
+ }
}
//====================================================================================================
@@ -1158,13 +1271,25 @@ public class ClassInfo_Test extends TestBase {
//====================================================================================================
@Test
void a035_getNameCanonical() {
+ // Test line 1112: inner != null && ! isParameterizedType
(branch 1)
assertEquals("org.apache.juneau.commons.reflect.AClass",
aClass.getNameCanonical());
assertEquals("org.apache.juneau.commons.reflect.ClassInfo_Test.A1",
aTypeInfo.getNameCanonical());
+ // Test line 1112: inner != null && isParameterizedType (branch
2)
// For ParameterizedType, should return null
assertNull(pTypeInfo.getNameCanonical());
assertNull(pTypeDimensionalInfo.getNameCanonical());
assertNull(pTypeGenericInfo.getNameCanonical());
+
+ // Test line 1112: inner == null && ! isParameterizedType
(branch 3)
+ // Create a ClassInfo with inner == null and innerType being a
TypeVariable (not ParameterizedType)
+ // aType is a TypeVariable from A2 extends Value<A1>
+ // By explicitly passing null for inner, we ensure inner == null
+ // TypeVariable is not a ParameterizedType, so
isParameterizedType == false
+ var typeVariableInfo = ClassInfo.of((Class<?>)null, aType);
+ // This should have inner == null and isParameterizedType ==
false
+ // So it should take branch 3: inner == null && !
isParameterizedType
+ assertNull(typeVariableInfo.getNameCanonical());
}
//====================================================================================================
@@ -1270,6 +1395,30 @@ public class ClassInfo_Test extends TestBase {
assertNotNull(formatted2);
// When inner is null but isParameterizedType is true, code
extracts raw type and uses its simple name
assertEquals("Map", formatted2);
+
+ // Test line 326: FULL format separator replacement (4 branches)
+ // Branch 1: separator != '$' && sb.indexOf("$") != -1 (true,
true) - should replace '$' with separator
+ var ci12 = ClassInfo.of(Map.Entry.class);
+ assertEquals("java.util.Map.Entry", ci12.getNameFormatted(FULL,
false, '.', BRACKETS));
+
+ // Branch 2: separator != '$' && sb.indexOf("$") == -1 (true,
false) - no '$' in name, no replacement needed
+ var ci13 = ClassInfo.of(String.class);
+ assertEquals("java.lang.String", ci13.getNameFormatted(FULL,
false, '.', BRACKETS));
+
+ // Branch 3: separator == '$' && sb.indexOf("$") != -1 (false,
true) - separator is '$', no replacement
+ assertEquals("java.util.Map$Entry", ci12.getNameFormatted(FULL,
false, '$', BRACKETS));
+
+ // Branch 4: separator == '$' && sb.indexOf("$") == -1 (false,
false) - separator is '$', no '$' in name
+ assertEquals("java.lang.String", ci13.getNameFormatted(FULL,
false, '$', BRACKETS));
+
+ // Test line 360: SIMPLE format with null class (not
ParameterizedType) - should use innerType.getTypeName()
+ // Use an existing TypeVariable from MC class which has type
parameters
+ var typeVar = MC.class.getTypeParameters()[0]; // MC<K,E> has K
as first type parameter
+ var ci14 = ClassInfo.of((Class<?>)null, typeVar);
+ var formatted3 = ci14.getNameFormatted(SIMPLE, false, '$',
BRACKETS);
+ assertNotNull(formatted3);
+ // Should use innerType.getTypeName() which returns the type
variable name
+ assertEquals(typeVar.getName(), formatted3);
}
//====================================================================================================
@@ -1481,6 +1630,19 @@ public class ClassInfo_Test extends TestBase {
check("java.util", pTypeDimensionalInfo.getPackage());
check("java.util", pTypeGenericInfo.getPackage());
check(null, pTypeGenericArgInfo.getPackage());
+
+ // Test line 229: packageInfo memoization with null inner
+ // When inner is null, opt(inner) is empty, so should return
null
+ var ci = ClassInfo.of((Class<?>)null, pType);
+ assertNull(ci.getPackage());
+
+ // Test with primitive types (getPackage() returns null)
+ var intCi = ClassInfo.of(int.class);
+ assertNull(intCi.getPackage()); // Primitives have no package
+
+ // Test with arrays of primitives (getPackage() returns null)
+ var intArrayCi = ClassInfo.of(int[].class);
+ assertNull(intArrayCi.getPackage()); // Arrays of primitives
have no package
}
//====================================================================================================
@@ -1553,6 +1715,13 @@ public class ClassInfo_Test extends TestBase {
// Nested type
check("MM", mn.getParameterType(1, HashMap.class));
+
+ // Note: Line 1375 (actualType3 =
(TypeVariable<?>)entry.getValue()) is executed when
+ // resolving a type variable in a nested inner class where the
value in the outer type map
+ // is itself a TypeVariable (not a Class). This requires a
complex nested generic scenario
+ // where type variables chain through multiple levels before
resolving to a Class.
+ // The existing tests cover the main resolution paths. Line
1375 handles the edge case
+ // where a type variable maps to another type variable that
needs further resolution.
}
//====================================================================================================
@@ -1596,21 +1765,60 @@ public class ClassInfo_Test extends TestBase {
assertTrue(names.contains("ISuperGrandParent"), "Should include
ISuperGrandParent from IGrandParent hierarchy");
}
+ // Sealed class for testing getPermittedSubclasses (Java 17+)
+ // Only compile if sealed classes are available
+ @SuppressWarnings("unused")
+ public static sealed class SealedTestClass permits SealedSubclass1,
SealedSubclass2 {
+ }
+
+ @SuppressWarnings("unused")
+ public static final class SealedSubclass1 extends SealedTestClass {
+ }
+
+ @SuppressWarnings("unused")
+ public static final class SealedSubclass2 extends SealedTestClass {
+ }
+
+ // Record class for testing isRecord (Java 14+)
+ // Only compile if records are available
+ @SuppressWarnings("unused")
+ public static record TestRecord(String name, int value) {
+ }
+
//====================================================================================================
// getPermittedSubclasses()
//====================================================================================================
@Test
void a050_getPermittedSubclasses() {
- // Most classes are not sealed, so should return empty list
+ // Most classes are not sealed, so should return empty list
(line 1463: ! inner.isSealed())
var permitted = aClass.getPermittedSubclasses();
assertNotNull(permitted);
assertTrue(permitted.isEmpty());
- // For types with null inner, should return empty list
+ // For types with null inner, should return empty list (line
1463: inner == null)
var ci = ClassInfo.of((Class<?>)null, pType);
var empty = ci.getPermittedSubclasses();
assertNotNull(empty);
assertTrue(empty.isEmpty());
+
+ // Test line 1465: inner != null && inner.isSealed() - sealed
class with permitted subclasses
+ // Only test if sealed classes are available (Java 17+)
+ try {
+ Class.forName("java.lang.constant.Constable");
+ // Sealed classes are available
+ var sealedInfo = ClassInfo.of(SealedTestClass.class);
+ var permittedSubclasses =
sealedInfo.getPermittedSubclasses();
+ assertNotNull(permittedSubclasses);
+ assertFalse(permittedSubclasses.isEmpty());
+ // Should contain the permitted subclasses
+ var subclassNames = permittedSubclasses.stream()
+ .map(ClassInfo::getNameSimple)
+ .collect(Collectors.toSet());
+ assertTrue(subclassNames.contains("SealedSubclass1"));
+ assertTrue(subclassNames.contains("SealedSubclass2"));
+ } catch (ClassNotFoundException e) {
+ // Sealed classes not available (Java < 17), skip this
test
+ }
}
//====================================================================================================
@@ -1711,6 +1919,16 @@ public class ClassInfo_Test extends TestBase {
// Test on types
check("", aTypeInfo.getPublicFields());
+ check("", pTypeGenericArgInfo.getPublicFields());
+
+ // Test line 249: publicFields memoization
+ // This line uses parents.get().stream() which requires inner
to be non-null
+ // When inner is null, parents.get() will return empty list, so
publicFields should be empty
+ // However, since parents depends on inner, we need to test
with a class that has no public fields
+ // to ensure the stream operations are covered
+ var emptyFields = ClassInfo.of(Object.class).getPublicFields();
+ assertNotNull(emptyFields);
+ // Object class has no public fields (except in some JVM
implementations)
}
//====================================================================================================
@@ -1788,6 +2006,14 @@ public class ClassInfo_Test extends TestBase {
// Records not available, skip test
assertTrue(cc3.getRecordComponents().isEmpty());
}
+
+ // Test line 240: recordComponents memoization
+ // When inner is null, opt(inner) is empty, so should return
empty list
+ var ci = ClassInfo.of((Class<?>)null, pType);
+ assertTrue(ci.getRecordComponents().isEmpty());
+
+ // When inner is not null but isRecord() is false, filter
should return empty, so should return empty list
+ assertTrue(aClass.getRecordComponents().isEmpty());
}
//====================================================================================================
@@ -1800,6 +2026,37 @@ public class ClassInfo_Test extends TestBase {
var method = repeatable.getRepeatedAnnotationMethod();
// @Repeatable itself is not repeatable, so should return null
assertNull(method);
+
+ // Test isRepeatedAnnotation() (line 2135)
+ // When getRepeatedAnnotationMethod() returns null,
isRepeatedAnnotation() should return false
+ assertFalse(repeatable.isRepeatedAnnotation());
+
+ // Test with a class that has a repeatable annotation method
+ // TestRepeatableContainer is the container annotation for
TestRepeatable
+ // It has a value() method that returns TestRepeatable[], and
TestRepeatable is marked with @Repeatable(TestRepeatableContainer.class)
+ var container = ClassInfo.of(TestRepeatableContainer.class);
+ var containerMethod = container.getRepeatedAnnotationMethod();
+ assertNotNull(containerMethod); // Should find the value()
method
+ assertTrue(container.isRepeatedAnnotation()); // Line 2135:
getRepeatedAnnotationMethod() != null returns true
+
+ // Test line 2364 branches: return r != null &&
r.value().equals(inner);
+ // Branch 1: r != null is false (r is null) - when component
type doesn't have @Repeatable
+ // This is covered by NonRepeatableArrayContainer which has
value() returning String[], but String is not a repeatable annotation
+ var nonRepeatableContainer =
ClassInfo.of(NonRepeatableArrayContainer.class);
+
assertNull(nonRepeatableContainer.getRepeatedAnnotationMethod()); // Should
return null because String is not repeatable
+
+ // Branch 2: r != null is true, r.value().equals(inner) is true
- covered by TestRepeatableContainer above
+ // TestRepeatableContainer has value() returning
TestRepeatable[], and TestRepeatable is marked with
@Repeatable(TestRepeatableContainer.class)
+ // So when checking TestRepeatableContainer, r.value() equals
TestRepeatableContainer.class (inner)
+
+ // Branch 3: r != null is true, r.value().equals(inner) is
false - when @Repeatable points to a different container
+ // WrongContainer has value() returning TestRepeatable[], but
TestRepeatable's @Repeatable points to TestRepeatableContainer, not
WrongContainer
+ // So when checking WrongContainer, r.value() would be
TestRepeatableContainer.class, not WrongContainer.class, so equals(inner) is
false
+ var wrongContainer = ClassInfo.of(WrongContainer.class);
+ assertNull(wrongContainer.getRepeatedAnnotationMethod()); //
Should return null because the @Repeatable points to a different container
+
+ // Test that non-repeatable classes return false
+ assertFalse(aClass.isRepeatedAnnotation());
}
//====================================================================================================
@@ -1811,6 +2068,15 @@ public class ClassInfo_Test extends TestBase {
assertNotNull(signers);
// Most classes won't have signers unless from a signed JAR
assertTrue(signers.isEmpty() || !signers.isEmpty());
+
+ // Test line 244: signers memoization
+ // When inner is null, opt(inner) is empty, so should return
empty list
+ var ci = ClassInfo.of((Class<?>)null, pType);
+ assertTrue(ci.getSigners().isEmpty());
+
+ // When inner is not null but getSigners() returns null, map
should handle null and return empty list
+ // Most classes return null from getSigners(), which is then
wrapped in u(l(x)) to create empty list
+ // This is already tested above with aClass.getSigners()
}
//====================================================================================================
@@ -1969,6 +2235,12 @@ public class ClassInfo_Test extends TestBase {
assertTrue(ClassInfo.of(A.class).is(ANNOTATION));
assertFalse(aClass.is(ANNOTATION));
+ // NOT_ANNOTATION (line 1773) - test both branches
+ // Branch 1: isAnnotation() returns false, so NOT_ANNOTATION
returns true
+ assertTrue(aClass.is(NOT_ANNOTATION));
+ // Branch 2: isAnnotation() returns true, so NOT_ANNOTATION
returns false
+ assertFalse(ClassInfo.of(A.class).is(NOT_ANNOTATION));
+
// ANONYMOUS and NOT_ANONYMOUS (lines 1774, 1775)
// Anonymous classes are created dynamically, so we test
NOT_ANONYMOUS
assertTrue(aClass.is(NOT_ANONYMOUS));
@@ -1984,10 +2256,22 @@ public class ClassInfo_Test extends TestBase {
assertTrue(ClassInfo.of(String[].class).is(ARRAY));
assertFalse(aClass.is(ARRAY));
+ // NOT_ARRAY (line 1777) - test both branches
+ // Branch 1: isArray() returns false, so NOT_ARRAY returns true
+ assertTrue(aClass.is(NOT_ARRAY));
+ // Branch 2: isArray() returns true, so NOT_ARRAY returns false
+ assertFalse(ClassInfo.of(String[].class).is(NOT_ARRAY));
+
// ENUM (line 1781)
assertTrue(ClassInfo.of(ClassArrayFormat.class).is(ENUM));
assertFalse(aClass.is(ENUM));
+ // NOT_ENUM (line 1782) - test both branches
+ // Branch 1: isEnum() returns false, so NOT_ENUM returns true
+ assertTrue(aClass.is(NOT_ENUM));
+ // Branch 2: isEnum() returns true, so NOT_ENUM returns false
+ assertFalse(ClassInfo.of(ClassArrayFormat.class).is(NOT_ENUM));
+
// LOCAL and NOT_LOCAL (line 1783)
// Local class
class LocalTestClass {}
@@ -2009,6 +2293,10 @@ public class ClassInfo_Test extends TestBase {
assertTrue(ClassInfo.of(int.class).is(PRIMITIVE));
assertFalse(aClass.is(PRIMITIVE));
+ // NOT_PRIMITIVE (line 1790)
+ assertTrue(aClass.is(NOT_PRIMITIVE));
+ assertFalse(ClassInfo.of(int.class).is(NOT_PRIMITIVE));
+
// RECORD (line 1791) - test if records are available
try {
Class.forName("java.lang.Record");
@@ -2019,15 +2307,59 @@ public class ClassInfo_Test extends TestBase {
// Records not available, skip
}
+ // NOT_RECORD (line 1792) - test both branches
+ // Branch 1: isRecord() returns false, so NOT_RECORD returns
true
+ assertTrue(aClass.is(NOT_RECORD));
+ // Branch 2: isRecord() returns true, so NOT_RECORD returns
false
+ try {
+ Class.forName("java.lang.Record");
+ // Records are available (Java 14+)
+ var recordInfo = ClassInfo.of(TestRecord.class);
+ assertTrue(recordInfo.is(RECORD));
+ assertFalse(recordInfo.is(NOT_RECORD));
+ } catch (ClassNotFoundException e) {
+ // Records not available, skip
+ }
+
// SEALED (line 1793) - test if sealed classes are available
try {
Class.forName("java.lang.constant.Constable");
// Sealed classes are available (Java 17+)
// Most classes are not sealed, so should return false
assertFalse(aClass.is(SEALED));
+ // Test with actual sealed class
+ var sealedInfo = ClassInfo.of(SealedTestClass.class);
+ assertTrue(sealedInfo.is(SEALED));
} catch (ClassNotFoundException e) {
// Sealed classes not available, skip
}
+
+ // NOT_SEALED (line 1794) - test both branches
+ // Branch 1: isSealed() returns false, so NOT_SEALED returns
true
+ assertTrue(aClass.is(NOT_SEALED));
+ // Branch 2: isSealed() returns true, so NOT_SEALED returns
false
+ try {
+ Class.forName("java.lang.constant.Constable");
+ // Sealed classes are available (Java 17+)
+ var sealedInfo = ClassInfo.of(SealedTestClass.class);
+ assertFalse(sealedInfo.is(NOT_SEALED));
+ } catch (ClassNotFoundException e) {
+ // Sealed classes not available, skip
+ }
+
+ // SYNTHETIC (line 1795) - synthetic classes are
compiler-generated
+ // Most regular classes are not synthetic
+ assertFalse(aClass.is(SYNTHETIC));
+ // Inner classes and anonymous classes can be synthetic
+ // Anonymous classes are typically synthetic
+ if (anonymousInfo.isAnonymousClass()) {
+ // Anonymous classes may or may not be synthetic
depending on JVM
+ // Just verify the method doesn't throw
+ anonymousInfo.is(SYNTHETIC);
+ }
+
+ // NOT_SYNTHETIC (line 1796)
+ assertTrue(aClass.is(NOT_SYNTHETIC));
}
//====================================================================================================
@@ -2402,18 +2734,20 @@ public class ClassInfo_Test extends TestBase {
//====================================================================================================
@Test
void a088b_isNestmateOf() {
- // Same class is nestmate of itself
+ // Same class is nestmate of itself (line 1957: nn(this.inner)
= true, nn(c) = true, isNestmateOf(c) = true)
assertTrue(aClass.isNestmateOf(AClass.class));
- // Different classes in same package may or may not be nestmates
- // (depends on whether they're in the same nest)
+ // Different classes that are NOT nestmates (line 1957:
nn(this.inner) = true, nn(c) = true, isNestmateOf(c) = false)
+ // Top-level classes in the same package are typically not
nestmates unless they're in the same nest
+ assertFalse(aClass.isNestmateOf(String.class));
+
assertFalse(ClassInfo.of(String.class).isNestmateOf(AClass.class));
- // For types with null inner, should return false
+ // For types with null inner, should return false (line 1957:
nn(this.inner) = false, short-circuit)
var ci = ClassInfo.of((Class<?>)null, pType);
assertFalse(ci.isNestmateOf(AClass.class));
assertFalse(ci.isNestmateOf(null));
- // With null argument, should return false
+ // With null argument, should return false (line 1957:
nn(this.inner) = true, nn(c) = false, short-circuit)
assertFalse(aClass.isNestmateOf(null));
}
@@ -2573,6 +2907,18 @@ public class ClassInfo_Test extends TestBase {
assertFalse(ClassInfo.of(String.class).isParentOfLenient((ClassInfo)null));
var nullInnerCi = ClassInfo.of((Class<?>)null, pType);
assertFalse(nullInnerCi.isParentOfLenient(ClassInfo.of(String.class)));
+
+ // Test all branches of line 2087: if (this.isPrimitive() ||
child.isPrimitive())
+ // Branch 1: this.isPrimitive() == true, child.isPrimitive() ==
false (already covered above)
+ // Branch 2: this.isPrimitive() == false, child.isPrimitive()
== true (already covered above)
+ // Branch 3: this.isPrimitive() == true, child.isPrimitive() ==
true
+ // Note: If both are the same primitive, isAssignableFrom
returns true, so we return early at line 2086
+ // To reach line 2087 with both primitives, we need different
primitive types
+
assertFalse(ClassInfo.of(int.class).isParentOfLenient(ClassInfo.of(long.class)));
+ // Same primitive type (returns true at line 2086, doesn't
reach line 2087)
+
assertTrue(ClassInfo.of(int.class).isParentOfLenient(int.class));
+
assertTrue(ClassInfo.of(int.class).isParentOfLenient(ClassInfo.of(int.class)));
+ // Branch 4: this.isPrimitive() == false, child.isPrimitive()
== false (already covered by String tests)
}
//====================================================================================================
@@ -2619,6 +2965,10 @@ public class ClassInfo_Test extends TestBase {
// Records not available, skip test
assertFalse(cc3.isRecord());
}
+
+ // Test with null inner (line 2120)
+ var ci = ClassInfo.of((Class<?>)null, pType);
+ assertFalse(ci.isRecord());
}
//====================================================================================================
@@ -2688,6 +3038,23 @@ public class ClassInfo_Test extends TestBase {
var anonymousInfo = ClassInfo.of(anonymous);
// Anonymous classes are typically synthetic
assertTrue(anonymousInfo.isSynthetic() ||
!anonymousInfo.isSynthetic());
+
+ // Test with null inner (line 2169)
+ var ci = ClassInfo.of((Class<?>)null, pType);
+ assertFalse(ci.isSynthetic());
+ }
+
+
//====================================================================================================
+ // isSealed()
+
//====================================================================================================
+ @Test
+ void a097b_isSealed() {
+ // Test with null inner (line 2149)
+ var ci = ClassInfo.of((Class<?>)null, pType);
+ assertFalse(ci.isSealed());
+
+ // Test with regular classes (most are not sealed)
+ assertFalse(aClass.isSealed());
}
//====================================================================================================
@@ -2777,6 +3144,18 @@ public class ClassInfo_Test extends TestBase {
assertNotNull(info);
assertNull(info.inner());
assertNotNull(info.innerType());
+
+ // Test line 226: isParameterizedType initialization
+ // When innerType is null, isParameterizedType should be false
+ var nonParamType = ClassInfo.of(String.class);
+ // String.class is not a ParameterizedType, so
isParameterizedType should be false
+ // We can't directly access isParameterizedType, but we can
infer it from behavior
+
+ // When innerType is a ParameterizedType, isParameterizedType
should be true
+ // pTypeInfo has a ParameterizedType, so isParameterizedType
should be true
+ // We can verify this indirectly by checking that it behaves as
a parameterized type
+ assertNotNull(pTypeInfo);
+ assertNotNull(pTypeInfo.innerType());
}
//====================================================================================================
@@ -2788,6 +3167,16 @@ public class ClassInfo_Test extends TestBase {
var info = ClassInfo.ofProxy(obj);
assertNotNull(info);
assertEquals(A1.class, info.inner());
+
+ // Test line 175: when getProxyFor returns null, should call
ClassInfo.of(object)
+ // Most objects are not proxies, so getProxyFor should return
null
+ // This tests the branch: inner == null ? ClassInfo.of(object)
: ClassInfo.of(inner)
+ var obj2 = new A1();
+ var info2 = ClassInfo.ofProxy(obj2);
+ assertNotNull(info2);
+ assertEquals(A1.class, info2.inner());
+ // Should be the same as calling ClassInfo.of directly
+ assertEquals(ClassInfo.of(A1.class), info2);
}
//====================================================================================================
@@ -2819,6 +3208,64 @@ public class ClassInfo_Test extends TestBase {
mi2 = ClassInfo.of(A6.class).getPublicMethod(x ->
x.hasName("m2")).get();
check("A1",
mi2.getParameter(0).getParameterType().unwrap(Value.class));
check("A1", mi2.getReturnType().unwrap(Value.class));
+
+ // Test unwrap with ParameterizedType (line 2382)
+ // Create a ParameterizedType directly
+ var pTypeOptional = new java.lang.reflect.ParameterizedType() {
+ @Override
+ public Type[] getActualTypeArguments() {
+ return new Type[]{A1.class};
+ }
+ @Override
+ public Type getRawType() {
+ return Optional.class;
+ }
+ @Override
+ public Type getOwnerType() {
+ return null;
+ }
+ };
+ var ciOptional = ClassInfo.of((Class<?>)null, pTypeOptional);
+ check("A1", ciOptional.unwrap(Optional.class));
+
+ // Test unwrap with ParameterizedType that has no type
arguments (line 2383)
+ var pTypeEmpty = new java.lang.reflect.ParameterizedType() {
+ @Override
+ public Type[] getActualTypeArguments() {
+ return new Type[0];
+ }
+ @Override
+ public Type getRawType() {
+ return Optional.class;
+ }
+ @Override
+ public Type getOwnerType() {
+ return null;
+ }
+ };
+ var ciEmpty = ClassInfo.of((Class<?>)null, pTypeEmpty);
+ // Should return itself since there are no type arguments
+ assertSame(ciEmpty, ciEmpty.unwrap(Optional.class));
+
+ // Test unwrap with Class that extends wrapper (line 2387, 2388)
+ // A2 extends Value<A1>, so unwrap should work
+ // This covers: innerType instanceof Class<?> is true,
innerType3 != parameterizedType is true, isAssignableFrom is true
+ check("A1", of(A2.class).unwrap(Value.class));
+
+ // Test unwrap with Class that doesn't extend wrapper (line
2388 - false branch)
+ // A1 doesn't extend Value, so unwrap should return itself
+ // This covers: innerType instanceof Class<?> is true,
innerType3 != parameterizedType is true, isAssignableFrom is false
+ assertSame(of(A1.class), of(A1.class).unwrap(Value.class));
+
+ // Test unwrap when innerType3 == parameterizedType (line 2388
- false branch of !=)
+ // When unwrapping Value.class from Value.class itself,
innerType3 == parameterizedType, so should return itself
+ // This covers: innerType instanceof Class<?> is true,
innerType3 != parameterizedType is false (short-circuit)
+ assertSame(of(Value.class),
of(Value.class).unwrap(Value.class));
+
+ // Test unwrap when innerType is not a Class<?> (line 2387 -
false branch)
+ // When innerType is a ParameterizedType, the else if branch is
not entered
+ // This is already covered by the ParameterizedType tests
above, but let's verify
+ // The test with ciOptional above covers this case
}
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ElementInfo_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ElementInfo_Test.java
index 864061cbf5..a6dbcc83a1 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ElementInfo_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ElementInfo_Test.java
@@ -267,6 +267,14 @@ class ElementInfo_Test extends TestBase {
assertFalse(publicClass.isAll(PRIVATE));
assertTrue(staticMethod.isAll(STATIC));
assertFalse(staticMethod.isAll(FINAL));
+
+ // Multiple flags where first doesn't match (short-circuit) -
line 143
+ // allMatch short-circuits on first false, so this tests the
short-circuit branch
+ assertFalse(publicClass.isAll(PRIVATE, PUBLIC, NOT_PRIVATE));
+
+ // Multiple flags where all match (full iteration) - line 143
+ // allMatch iterates through all elements when all are true
+ assertTrue(publicClass.isAll(PUBLIC, NOT_PRIVATE,
NOT_PROTECTED, NOT_FINAL));
}
//====================================================================================================
@@ -296,6 +304,14 @@ class ElementInfo_Test extends TestBase {
assertFalse(publicClass.isAny(PRIVATE));
assertTrue(staticMethod.isAny(STATIC));
assertFalse(staticMethod.isAny(FINAL));
+
+ // Multiple flags where first matches (short-circuit) - line 157
+ // anyMatch short-circuits on first true, so this tests the
short-circuit branch
+ assertTrue(publicClass.isAny(PUBLIC, PRIVATE, PROTECTED));
+
+ // Multiple flags where none match (full iteration) - line 157
+ // anyMatch iterates through all elements when all are false
+ assertFalse(publicClass.isAny(PRIVATE, PROTECTED, FINAL,
ABSTRACT));
}
//====================================================================================================
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ExecutableInfo_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ExecutableInfo_Test.java
index 7b8117fc6b..d30f8963d1 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ExecutableInfo_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ExecutableInfo_Test.java
@@ -305,7 +305,7 @@ class ExecutableInfo_Test extends TestBase {
// getFullName()
//====================================================================================================
@Test
- void a010_getFullName() {
+ void a010_getFullName() throws Exception {
// Method
assertEquals("org.apache.juneau.commons.reflect.ExecutableInfo_Test$X.foo()",
x2.getPublicMethod(x -> x.hasName("foo") && x.getParameterCount() ==
0).get().getFullName());
assertEquals("org.apache.juneau.commons.reflect.ExecutableInfo_Test$X.foo(java.lang.String)",
x2.getPublicMethod(x -> x.hasName("foo") &&
x.hasParameterTypes(String.class)).get().getFullName());
@@ -315,6 +315,27 @@ class ExecutableInfo_Test extends TestBase {
assertEquals("org.apache.juneau.commons.reflect.ExecutableInfo_Test$X()",
x2.getPublicConstructor(cons -> cons.getParameterCount() ==
0).get().getFullName());
assertEquals("org.apache.juneau.commons.reflect.ExecutableInfo_Test$X(java.lang.String)",
x2.getPublicConstructor(x ->
x.hasParameterTypes(String.class)).get().getFullName());
assertEquals("org.apache.juneau.commons.reflect.ExecutableInfo_Test$X(java.util.Map<java.lang.String,java.lang.Object>)",
x2.getPublicConstructor(x ->
x.hasParameterTypes(Map.class)).get().getFullName());
+
+ // Test line 752: getPackage() returns null branch
+ // Primitive types don't have packages, but we can't get
methods/constructors from primitives.
+ // However, we can test with a class in the default package (no
package declaration).
+ // Note: Some classloaders may assign a package even to default
package classes.
+ try {
+ Class<?> defaultPkgClassType =
Class.forName("DefaultPackageTestClass");
+ ClassInfo defaultPkgClass =
ClassInfo.of(defaultPkgClassType);
+ PackageInfo pkg = defaultPkgClass.getPackage();
+ if (pkg == null) {
+ // Test the false branch of line 752: when
package is null, don't append package name
+ ConstructorInfo defaultPkgCtor =
defaultPkgClass.getPublicConstructor(cons -> cons.getParameterCount() ==
0).get();
+ String fullName = defaultPkgCtor.getFullName();
+ // When package is null, getFullName() should
not include package prefix
+
assertTrue(fullName.startsWith("DefaultPackageTestClass("), "Full name should
start with class name when package is null: " + fullName);
+ // Verify no package prefix (no dots before the
class name, except for inner classes)
+
assertFalse(fullName.matches("^[a-z][a-z0-9]*(\\.[a-z][a-z0-9]*)+\\."), "Full
name should not have package prefix when package is null: " + fullName);
+ }
+ } catch (ClassNotFoundException e) {
+ // If class not found, skip this part of the test
+ }
}
//====================================================================================================
@@ -524,6 +545,20 @@ class ExecutableInfo_Test extends TestBase {
var longClass = ClassInfo.of(long.class);
assertTrue(e_hasParams.hasParameterTypes(intClass));
assertFalse(e_hasParams.hasParameterTypes(longClass));
+
+ // Test line 484 branches:
+ // Branch 1: params.size() == args.length is false
(short-circuit) - when sizes don't match
+ // e_hasParams has 1 parameter, so passing 0 or 2 args should
return false
+ assertFalse(e_hasParams.hasParameterTypes(new ClassInfo[0]));
// Empty args array, but method has 1 parameter
+ var intClass2 = ClassInfo.of(int.class);
+ assertFalse(e_hasParams.hasParameterTypes(intClass,
intClass2)); // 2 args, but method has 1 parameter
+
+ // Branch 2: params.size() == args.length is true, args.length
== 0 (empty stream, allMatch returns true)
+ // e_hasNoParams has 0 parameters, so passing empty args should
return true
+ assertTrue(e_hasNoParams.hasParameterTypes(new ClassInfo[0]));
// Empty args array, method has 0 parameters
+
+ // Branch 3: params.size() == args.length is true, args.length
> 0, allMatch returns true - already covered above
+ // Branch 4: params.size() == args.length is true, args.length
> 0, allMatch returns false - already covered above
}
//====================================================================================================
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/FieldInfo_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/FieldInfo_Test.java
index 3f8c39eabb..2075afb3b9 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/FieldInfo_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/FieldInfo_Test.java
@@ -336,9 +336,30 @@ class FieldInfo_Test extends TestBase {
// getFullName()
//====================================================================================================
@Test
- void a010_getFullName() {
+ void a010_getFullName() throws Exception {
String fullName1 = g_field1.getFullName();
String fullName2 = g_field2.getFullName();
+
+ // Test line 449: getPackage() returns null (default package
class)
+ // A field can have a null package if its declaring class is in
the default package
+ // According to Java API, Class.getPackage() returns null for
classes in the default package
+ try {
+ Class<?> defaultPkgClassType =
Class.forName("DefaultPackageTestClass");
+ ClassInfo defaultPkgClass =
ClassInfo.of(defaultPkgClassType);
+ PackageInfo pkg = defaultPkgClass.getPackage();
+ if (pkg == null) {
+ // Test the false branch of line 449: when
package is null, don't append package name
+ FieldInfo defaultPkgField =
defaultPkgClass.getPublicField(x -> x.hasName("testField")).get();
+ String fullName = defaultPkgField.getFullName();
+ // When package is null, getFullName() should
not include package prefix
+
assertTrue(fullName.startsWith("DefaultPackageTestClass"), "Full name should
start with class name when package is null: " + fullName);
+ assertTrue(fullName.endsWith(".testField"),
"Full name should end with field name: " + fullName);
+ // Verify no package prefix (no dots before the
class name, except for inner classes)
+
assertFalse(fullName.matches("^[a-z][a-z0-9]*(\\.[a-z][a-z0-9]*)+\\."), "Full
name should not have package prefix when package is null: " + fullName);
+ }
+ } catch (ClassNotFoundException e) {
+ // If class not found, skip this part of the test
+ }
assertTrue(fullName1.endsWith("FieldInfo_Test$G.field1"));
assertTrue(fullName2.endsWith("FieldInfo_Test$G.field2"));
@@ -434,7 +455,8 @@ class FieldInfo_Test extends TestBase {
// Enum constant
assertTrue(testEnum_value1.is(ENUM_CONSTANT));
assertFalse(a1_f1.is(ENUM_CONSTANT));
- assertTrue(a1_f1.is(NOT_ENUM_CONSTANT));
+ assertTrue(a1_f1.is(NOT_ENUM_CONSTANT)); // Line 313: true
branch - field is NOT an enum constant
+ assertFalse(testEnum_value1.is(NOT_ENUM_CONSTANT)); // Line
313: false branch - field IS an enum constant
// Synthetic (lines 314-315)
assertFalse(a1_f1.is(SYNTHETIC));