diff --git a/testng-core-api/src/main/java/org/testng/internal/annotations/IAnnotationFinder.java b/testng-core-api/src/main/java/org/testng/internal/annotations/IAnnotationFinder.java index c1754b1ab7..e7113e2e44 100644 --- a/testng-core-api/src/main/java/org/testng/internal/annotations/IAnnotationFinder.java +++ b/testng-core-api/src/main/java/org/testng/internal/annotations/IAnnotationFinder.java @@ -3,6 +3,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.List; +import org.jspecify.annotations.Nullable; import org.testng.ITestNGMethod; import org.testng.annotations.IAnnotation; import org.testng.internal.ConstructorOrMethod; @@ -19,7 +20,7 @@ public interface IAnnotationFinder { * @param The expected {@link IAnnotation} type * @return The annotation on the class or null if none found. */ - A findAnnotation(Class cls, Class annotationClass); + @Nullable A findAnnotation(Class cls, Class annotationClass); /** * @param m - The corresponding {@link Method} @@ -28,14 +29,15 @@ public interface IAnnotationFinder { * @return The annotation on the method. If not found, return the annotation on the declaring * class. If not found, return null. */ - A findAnnotation(Method m, Class annotationClass); + @Nullable A findAnnotation(Method m, Class annotationClass); - A findAnnotation(ITestNGMethod m, Class annotationClass); + @Nullable A findAnnotation(ITestNGMethod m, Class annotationClass); - A findAnnotation(ConstructorOrMethod com, Class annotationClass); + @Nullable A findAnnotation( + ConstructorOrMethod com, Class annotationClass); - A findAnnotation( - Class clazz, Method m, java.lang.Class annotationClass); + @Nullable A findAnnotation( + @Nullable Class clazz, Method m, java.lang.Class annotationClass); /** * @param cons - The corresponding {@link Constructor} @@ -44,7 +46,7 @@ A findAnnotation( * @return The annotation on the method. If not found, return the annotation on the declaring * class. If not found, return null. */ - A findAnnotation(Constructor cons, Class annotationClass); + @Nullable A findAnnotation(Constructor cons, Class annotationClass); /** * @param cls - The corresponding class. diff --git a/testng-core-api/src/main/java/org/testng/internal/annotations/IDataProvidable.java b/testng-core-api/src/main/java/org/testng/internal/annotations/IDataProvidable.java index 075fd9cf3b..43c6c9c650 100644 --- a/testng-core-api/src/main/java/org/testng/internal/annotations/IDataProvidable.java +++ b/testng-core-api/src/main/java/org/testng/internal/annotations/IDataProvidable.java @@ -1,14 +1,18 @@ package org.testng.internal.annotations; +import org.jspecify.annotations.Nullable; + /** A trait shared by all the annotations that have dataProvider/dataProviderClass attributes. */ public interface IDataProvidable { String getDataProvider(); void setDataProvider(String v); + /** @return The class holding the data provider, or {@code null} when none was named. */ + @Nullable Class getDataProviderClass(); - void setDataProviderClass(Class v); + void setDataProviderClass(@Nullable Class v); String getDataProviderDynamicClass(); diff --git a/testng-core-api/src/main/java/org/testng/internal/annotations/package-info.java b/testng-core-api/src/main/java/org/testng/internal/annotations/package-info.java new file mode 100644 index 0000000000..a24c81736d --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/internal/annotations/package-info.java @@ -0,0 +1,5 @@ +/** Reads TestNG's annotations off classes, methods and constructors, and models what they say. */ +@NullMarked +package org.testng.internal.annotations; + +import org.jspecify.annotations.NullMarked; diff --git a/testng-core/src/main/java/org/testng/internal/annotations/AnnotationHelper.java b/testng-core/src/main/java/org/testng/internal/annotations/AnnotationHelper.java index 1cb7a9cd04..05719c4547 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/AnnotationHelper.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/AnnotationHelper.java @@ -10,6 +10,7 @@ import java.util.Map; import java.util.Objects; import java.util.function.Predicate; +import org.jspecify.annotations.Nullable; import org.testng.ITestNGMethod; import org.testng.ITestObjectFactory; import org.testng.annotations.IAnnotation; @@ -63,27 +64,32 @@ private AnnotationHelper() { // Utility class.defeat instantiation. } - public static ITestAnnotation findTest(IAnnotationFinder finder, Class cls) { + public static @Nullable ITestAnnotation findTest(IAnnotationFinder finder, Class cls) { return finder.findAnnotation(cls, ITestAnnotation.class); } - public static ITestAnnotation findTest(IAnnotationFinder finder, Method m) { + public static @Nullable ITestAnnotation findTest(IAnnotationFinder finder, Method m) { return finder.findAnnotation(m, ITestAnnotation.class); } - public static ITestAnnotation findTest(IAnnotationFinder finder, ITestNGMethod m) { + public static @Nullable ITestAnnotation findTest(IAnnotationFinder finder, ITestNGMethod m) { return finder.findAnnotation(m, ITestAnnotation.class); } - public static IFactoryAnnotation findFactory(IAnnotationFinder finder, Method m) { + public static @Nullable IFactoryAnnotation findFactory(IAnnotationFinder finder, Method m) { return finder.findAnnotation(m, IFactoryAnnotation.class); } - public static IFactoryAnnotation findFactory(IAnnotationFinder finder, Constructor c) { + public static @Nullable IFactoryAnnotation findFactory( + IAnnotationFinder finder, Constructor c) { return finder.findAnnotation(c, IFactoryAnnotation.class); } - public static IConfigurationAnnotation findConfiguration( + /** + * @return The configuration annotation carried by the method, or {@code null} when it carries + * none. + */ + public static @Nullable IConfigurationAnnotation findConfiguration( IAnnotationFinder finder, ConstructorOrMethod m) { IConfigurationAnnotation result = null; boolean ignoreFailure = false; @@ -137,21 +143,22 @@ public static IConfigurationAnnotation findConfiguration( return result; } - public static IConfigurationAnnotation findConfiguration(IAnnotationFinder finder, Method m) { + public static @Nullable IConfigurationAnnotation findConfiguration( + IAnnotationFinder finder, Method m) { return findConfiguration(finder, new ConstructorOrMethod(m)); } private static IConfigurationAnnotation createConfiguration( - IConfigurationAnnotation bs, - IConfigurationAnnotation as, - IConfigurationAnnotation bt, - IConfigurationAnnotation at, - IConfigurationAnnotation bg, - IConfigurationAnnotation ag, - IConfigurationAnnotation bc, - IConfigurationAnnotation ac, - IConfigurationAnnotation bm, - IConfigurationAnnotation am) { + @Nullable IConfigurationAnnotation bs, + @Nullable IConfigurationAnnotation as, + @Nullable IConfigurationAnnotation bt, + @Nullable IConfigurationAnnotation at, + @Nullable IConfigurationAnnotation bg, + @Nullable IConfigurationAnnotation ag, + @Nullable IConfigurationAnnotation bc, + @Nullable IConfigurationAnnotation ac, + @Nullable IConfigurationAnnotation bm, + @Nullable IConfigurationAnnotation am) { ConfigurationAnnotation result = new ConfigurationAnnotation(); if (bs != null) { @@ -306,7 +313,7 @@ && isAnnotationPresent(annotationFinder, cls, ITestAnnotation.class)) { return vResult.values().toArray(new ITestNGMethod[0]); } - public static A findAnnotationSuperClasses( + public static @Nullable A findAnnotationSuperClasses( Class annotationClass, Class parameterClass) { Class c = parameterClass; while (c != null) { diff --git a/testng-core/src/main/java/org/testng/internal/annotations/BaseAnnotation.java b/testng-core/src/main/java/org/testng/internal/annotations/BaseAnnotation.java index ac43d7f6f3..6ec14f8e9f 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/BaseAnnotation.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/BaseAnnotation.java @@ -2,14 +2,15 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import org.jspecify.annotations.Nullable; public class BaseAnnotation { - private Class m_testClass; - private Method m_method; - private Constructor m_constructor; + private @Nullable Class m_testClass; + private @Nullable Method m_method; + private @Nullable Constructor m_constructor; - public Constructor getConstructor() { + public @Nullable Constructor getConstructor() { return m_constructor; } @@ -17,7 +18,7 @@ public void setConstructor(Constructor constructor) { m_constructor = constructor; } - public Method getMethod() { + public @Nullable Method getMethod() { return m_method; } @@ -25,7 +26,7 @@ public void setMethod(Method method) { m_method = method; } - public Class getTestClass() { + public @Nullable Class getTestClass() { return m_testClass; } diff --git a/testng-core/src/main/java/org/testng/internal/annotations/BaseBeforeAfter.java b/testng-core/src/main/java/org/testng/internal/annotations/BaseBeforeAfter.java index c4383eafe8..ac14ee83eb 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/BaseBeforeAfter.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/BaseBeforeAfter.java @@ -1,22 +1,24 @@ package org.testng.internal.annotations; +import org.jspecify.annotations.Nullable; + public class BaseBeforeAfter extends TestOrConfiguration implements IBaseBeforeAfter { private boolean m_alwaysRun = false; private boolean m_inheritGroups = true; private String[] m_beforeGroups = {}; private String[] m_afterGroups = {}; - private String m_description; + private @Nullable String m_description; /** @return the description */ @Override - public String getDescription() { + public @Nullable String getDescription() { return m_description; } /** @param description the description to set */ @Override - public void setDescription(String description) { + public void setDescription(@Nullable String description) { m_description = description; } diff --git a/testng-core/src/main/java/org/testng/internal/annotations/DataProviderAnnotation.java b/testng-core/src/main/java/org/testng/internal/annotations/DataProviderAnnotation.java index 680bcb6fb2..900ad921cc 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/DataProviderAnnotation.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/DataProviderAnnotation.java @@ -1,5 +1,6 @@ package org.testng.internal.annotations; +import java.util.Collections; import java.util.List; import org.testng.IRetryDataProvider; import org.testng.annotations.IDataProviderAnnotation; @@ -7,11 +8,12 @@ /** An implementation of IDataProvider. */ public class DataProviderAnnotation extends BaseAnnotation implements IDataProviderAnnotation { - private String m_name; + private String m_name = ""; private boolean m_parallel; - private List m_indices; + private List m_indices = Collections.emptyList(); private boolean m_bubbleUpFailures = false; - private Class retryUsing; + private Class retryUsing = + IRetryDataProvider.DisableDataProviderRetries.class; private boolean cachedDataForTestRetries = true; diff --git a/testng-core/src/main/java/org/testng/internal/annotations/DefaultAnnotationTransformer.java b/testng-core/src/main/java/org/testng/internal/annotations/DefaultAnnotationTransformer.java index d72a57afde..9eb899cdbb 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/DefaultAnnotationTransformer.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/DefaultAnnotationTransformer.java @@ -2,6 +2,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import org.jspecify.annotations.Nullable; import org.testng.IAnnotationTransformer; import org.testng.annotations.ITestAnnotation; @@ -15,7 +16,11 @@ public void transform( @Override public void transform( - ITestAnnotation annotation, Class testClass, Constructor cons, Method tm, Class clazz) { + ITestAnnotation annotation, + @Nullable Class testClass, + @Nullable Constructor cons, + @Nullable Method tm, + @Nullable Class clazz) { super.transform(annotation, testClass, cons, tm, clazz); } } diff --git a/testng-core/src/main/java/org/testng/internal/annotations/FactoryAnnotation.java b/testng-core/src/main/java/org/testng/internal/annotations/FactoryAnnotation.java index 3feddb35e5..e2f098097d 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/FactoryAnnotation.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/FactoryAnnotation.java @@ -1,17 +1,18 @@ package org.testng.internal.annotations; import java.util.List; +import org.jspecify.annotations.Nullable; import org.testng.annotations.IFactoryAnnotation; import org.testng.annotations.Lazy; /** An implementation of IFactory */ public class FactoryAnnotation extends BaseAnnotation implements IFactoryAnnotation { - private String m_dataProvider = null; - private Class m_dataProviderClass; - private String m_dataProviderDynamicClass; + private String m_dataProvider = ""; + private @Nullable Class m_dataProviderClass; + private String m_dataProviderDynamicClass = ""; private boolean m_enabled = true; - private List m_indices; + private @Nullable List m_indices; private Lazy m_lazy = Lazy.UNSET; @Override @@ -24,12 +25,12 @@ public void setDataProvider(String dataProvider) { m_dataProvider = dataProvider; } - public void setDataProviderClass(Class dataProviderClass) { + public void setDataProviderClass(@Nullable Class dataProviderClass) { m_dataProviderClass = dataProviderClass; } @Override - public Class getDataProviderClass() { + public @Nullable Class getDataProviderClass() { return m_dataProviderClass; } @@ -54,7 +55,7 @@ public void setEnabled(boolean enabled) { } @Override - public List getIndices() { + public @Nullable List getIndices() { return m_indices; } @@ -69,7 +70,7 @@ public Lazy getLazy() { } @Override - public void setLazy(Lazy lazy) { + public void setLazy(@Nullable Lazy lazy) { m_lazy = lazy == null ? Lazy.UNSET : lazy; } } diff --git a/testng-core/src/main/java/org/testng/internal/annotations/IAnnotationTransformer.java b/testng-core/src/main/java/org/testng/internal/annotations/IAnnotationTransformer.java index d3fa9fd035..3be32d839d 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/IAnnotationTransformer.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/IAnnotationTransformer.java @@ -2,6 +2,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import org.jspecify.annotations.Nullable; import org.testng.annotations.ITestAnnotation; /** For backward compatibility. */ @@ -9,10 +10,10 @@ public interface IAnnotationTransformer extends org.testng.IAnnotationTransforme default void transform( ITestAnnotation annotation, - Class testClass, - Constructor testConstructor, - Method testMethod, - Class occurringClazz) { + @Nullable Class testClass, + @Nullable Constructor testConstructor, + @Nullable Method testMethod, + @Nullable Class occurringClazz) { // not implemented } } diff --git a/testng-core/src/main/java/org/testng/internal/annotations/IBaseBeforeAfter.java b/testng-core/src/main/java/org/testng/internal/annotations/IBaseBeforeAfter.java index 240bc3eddb..78080c6f59 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/IBaseBeforeAfter.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/IBaseBeforeAfter.java @@ -1,5 +1,6 @@ package org.testng.internal.annotations; +import org.jspecify.annotations.Nullable; import org.testng.annotations.ITestOrConfiguration; /** Base interface for IBeforeSuite, IAfterSuite, etc... */ @@ -46,6 +47,7 @@ public interface IBaseBeforeAfter extends ITestOrConfiguration { * The description for this method. The string used will appear in the HTML report and also on * standard output if verbose > 2. */ + @Nullable String getDescription(); default boolean ignoreFailure() { diff --git a/testng-core/src/main/java/org/testng/internal/annotations/IgnoreListener.java b/testng-core/src/main/java/org/testng/internal/annotations/IgnoreListener.java index 8af2a78903..e87150c9c5 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/IgnoreListener.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/IgnoreListener.java @@ -3,6 +3,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Arrays; +import org.jspecify.annotations.Nullable; import org.testng.annotations.ITestAnnotation; import org.testng.annotations.Ignore; import org.testng.internal.reflect.ReflectionHelper; @@ -18,10 +19,10 @@ public void transform( @Override public void transform( ITestAnnotation annotation, - Class testClass, - Constructor tc, - Method testMethod, - Class clazz) { + @Nullable Class testClass, + @Nullable Constructor tc, + @Nullable Method testMethod, + @Nullable Class clazz) { if (!annotation.getEnabled()) { return; } @@ -34,7 +35,7 @@ public void transform( ignoreTestAtClass(clazz, annotation); } - private static void ignoreTestAtClass(Class clazz, ITestAnnotation annotation) { + private static void ignoreTestAtClass(@Nullable Class clazz, ITestAnnotation annotation) { if (clazz != null) { ignoreTest(annotation, ReflectionHelper.findAnnotation(clazz, Ignore.class)); Package testPackage = clazz.getPackage(); @@ -44,7 +45,7 @@ private static void ignoreTestAtClass(Class clazz, ITestAnnotation annotation } } - private static void ignoreTest(ITestAnnotation annotation, Ignore ignore) { + private static void ignoreTest(ITestAnnotation annotation, @Nullable Ignore ignore) { if (ignore == null) { return; } @@ -66,7 +67,7 @@ private static void updateDescription(ITestAnnotation annotation, Ignore ignore) } @SuppressWarnings("deprecation") - private static Ignore findAnnotation(Package testPackage) { + private static @Nullable Ignore findAnnotation(@Nullable Package testPackage) { if (testPackage == null) { return null; } diff --git a/testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java b/testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java index 8651ae1c43..ad612e9664 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import org.jspecify.annotations.Nullable; import org.testng.IAnnotationTransformer; import org.testng.ITestNGMethod; import org.testng.annotations.AfterClass; @@ -75,7 +76,8 @@ public JDK15AnnotationFinder(IAnnotationTransformer transformer) { m_annotationMap.put(IAfterMethod.class, AfterMethod.class); } - private A findAnnotationInSuperClasses(Class cls, Class a) { + private @Nullable A findAnnotationInSuperClasses( + Class cls, Class a) { // Hack for @Listeners: we don't look in superclasses for this annotation // because inheritance of this annotation causes aggregation instead of // overriding @@ -98,13 +100,13 @@ private A findAnnotationInSuperClasses(Class cls, Clas } @Override - public A findAnnotation(Method m, Class annotationClass) { + public @Nullable A findAnnotation(Method m, Class annotationClass) { return findAnnotation(null, m, annotationClass); } @Override - public A findAnnotation( - Class clazz, Method m, Class annotationClass) { + public @Nullable A findAnnotation( + @Nullable Class clazz, Method m, Class annotationClass) { final Class a = m_annotationMap.get(annotationClass); if (a == null) { throw new IllegalArgumentException( @@ -123,7 +125,8 @@ public A findAnnotation( } @Override - public A findAnnotation(ITestNGMethod tm, Class annotationClass) { + public @Nullable A findAnnotation( + ITestNGMethod tm, Class annotationClass) { final Class a = m_annotationMap.get(annotationClass); if (a == null) { throw new IllegalArgumentException( @@ -147,7 +150,7 @@ public A findAnnotation(ITestNGMethod tm, Class annot } @Override - public A findAnnotation( + public @Nullable A findAnnotation( ConstructorOrMethod com, Class annotationClass) { if (com.getConstructor() != null) { return findAnnotation(com.getConstructor(), annotationClass); @@ -159,11 +162,11 @@ public A findAnnotation( } private void transform( - IAnnotation a, - Class testClass, - Constructor testConstructor, - Method testMethod, - Class whichClass) { + @Nullable IAnnotation a, + @Nullable Class testClass, + @Nullable Constructor testConstructor, + @Nullable Method testMethod, + @Nullable Class whichClass) { if (!m_transformer.isEnabled()) { return; } @@ -192,19 +195,24 @@ private void transform( } @Override - public A findAnnotation(Class cls, Class annotationClass) { + public @Nullable A findAnnotation( + Class cls, Class annotationClass) { final Class a = m_annotationMap.get(annotationClass); if (a == null) { throw new IllegalArgumentException( "Java @Annotation class for '" + annotationClass + "' not found."); } Annotation annotation = findAnnotationInSuperClasses(cls, a); + if (annotation == null) { + return null; + } return findAnnotation( cls, annotation, annotationClass, cls, null, null, new Pair<>(annotation, cls), null); } @Override - public A findAnnotation(Constructor cons, Class annotationClass) { + public @Nullable A findAnnotation( + Constructor cons, Class annotationClass) { final Class a = m_annotationMap.get(annotationClass); if (a == null) { throw new IllegalArgumentException( @@ -266,15 +274,15 @@ private void findSuperInterface( } } - private A findAnnotation( + private @Nullable A findAnnotation( Class cls, - Annotation a, + @Nullable Annotation a, Class annotationClass, - Class testClass, - Constructor testConstructor, - Method testMethod, + @Nullable Class testClass, + @Nullable Constructor testConstructor, + @Nullable Method testMethod, Pair p, - Class whichClass) { + @Nullable Class whichClass) { if (a == null) { return null; } diff --git a/testng-core/src/main/java/org/testng/internal/annotations/JDK15TagFactory.java b/testng-core/src/main/java/org/testng/internal/annotations/JDK15TagFactory.java index b67ec38af0..895908dd0c 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/JDK15TagFactory.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/JDK15TagFactory.java @@ -6,7 +6,9 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; +import org.jspecify.annotations.Nullable; import org.testng.TestNGException; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterGroups; @@ -41,13 +43,15 @@ */ public class JDK15TagFactory { - public A createTag( - Class cls, Method method, Annotation a, Class annotationClass) { + public @Nullable A createTag( + Class cls, @Nullable Method method, Annotation a, Class annotationClass) { IAnnotation result = null; if (a != null) { if (annotationClass == IDataProviderAnnotation.class) { - result = createDataProviderTag(method, a); + Method dataProviderMethod = + Objects.requireNonNull(method, "@DataProvider is only ever looked up on a method"); + result = createDataProviderTag(dataProviderMethod, a); } else if (annotationClass == IFactoryAnnotation.class) { result = createFactoryTag(cls, a); } else if (annotationClass == IParametersAnnotation.class) { @@ -80,7 +84,8 @@ public A createTag( return (A) result; } - private IAnnotation maybeCreateNewConfigurationTag(Annotation a, Class annotationClass) { + private @Nullable IAnnotation maybeCreateNewConfigurationTag( + Annotation a, Class annotationClass) { IAnnotation result = null; if (annotationClass == IBeforeSuite.class) { @@ -577,7 +582,7 @@ private String[] join(String[] strings, String[] strings2) { * annotation don't allow nulls, so each type has a different way of defining its own default. */ interface Default { - boolean isDefault(T t); + boolean isDefault(@Nullable T t); } private static final Default> DEFAULT_CLASS = c -> c == Object.class; @@ -590,7 +595,7 @@ interface Default { * the hierarchy (Object). */ @SuppressWarnings("unchecked") - private T findInherited( + private @Nullable T findInherited( T methodValue, Class cls, Class annotationClass, @@ -642,7 +647,7 @@ private String[] findInheritedStringArray(Class cls, String methodName) { return result.toArray(new String[0]); } - private Object invokeMethod(Annotation test, String methodName) { + private @Nullable Object invokeMethod(Annotation test, String methodName) { Object result = null; try { // Note: we should cache methods already looked up diff --git a/testng-core/src/main/java/org/testng/internal/annotations/ListenersAnnotation.java b/testng-core/src/main/java/org/testng/internal/annotations/ListenersAnnotation.java index 658d827634..53aae91c05 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/ListenersAnnotation.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/ListenersAnnotation.java @@ -5,7 +5,12 @@ public class ListenersAnnotation implements IListeners, IAnnotation { - private Class[] m_value; + @SuppressWarnings("unchecked") + private static final Class[] NO_LISTENERS = + (Class[]) new Class[0]; + + // Listeners#value() defaults to {}; JDK15TagFactory sets the real value right after construction. + private Class[] m_value = NO_LISTENERS; @Override public Class[] getValue() { diff --git a/testng-core/src/main/java/org/testng/internal/annotations/TestAnnotation.java b/testng-core/src/main/java/org/testng/internal/annotations/TestAnnotation.java index cbdf3d26d1..d153bb49f2 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/TestAnnotation.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/TestAnnotation.java @@ -1,5 +1,6 @@ package org.testng.internal.annotations; +import org.jspecify.annotations.Nullable; import org.testng.IRetryAnalyzer; import org.testng.annotations.CustomAttribute; import org.testng.annotations.ITestAnnotation; @@ -18,9 +19,9 @@ public class TestAnnotation extends TestOrConfiguration implements ITestAnnotati private String m_suiteName = ""; private String m_testName = ""; private boolean m_singleThreaded = false; - private Class m_dataProviderClass = null; - private String m_dataProviderDynamicClass = null; - private Class m_retryAnalyzerClass = null; + private @Nullable Class m_dataProviderClass; + private String m_dataProviderDynamicClass = ""; + private Class m_retryAnalyzerClass = DisabledRetryAnalyzer.class; private boolean m_skipFailedInvocations = false; private boolean m_ignoreMissingDependencies = false; private CustomAttribute[] m_attributes = {}; @@ -58,12 +59,12 @@ public void setDataProvider(String dataProvider) { } @Override - public Class getDataProviderClass() { + public @Nullable Class getDataProviderClass() { return m_dataProviderClass; } @Override - public void setDataProviderClass(Class dataProviderClass) { + public void setDataProviderClass(@Nullable Class dataProviderClass) { m_dataProviderClass = dataProviderClass; } diff --git a/testng-core/src/main/java/org/testng/internal/annotations/TestOrConfiguration.java b/testng-core/src/main/java/org/testng/internal/annotations/TestOrConfiguration.java index f924b887e0..9141792912 100644 --- a/testng-core/src/main/java/org/testng/internal/annotations/TestOrConfiguration.java +++ b/testng-core/src/main/java/org/testng/internal/annotations/TestOrConfiguration.java @@ -1,5 +1,6 @@ package org.testng.internal.annotations; +import org.jspecify.annotations.Nullable; import org.testng.annotations.ITestOrConfiguration; public class TestOrConfiguration extends BaseAnnotation implements ITestOrConfiguration { @@ -8,7 +9,7 @@ public class TestOrConfiguration extends BaseAnnotation implements ITestOrConfig private boolean m_enabled = true; private String[] m_dependsOnGroups = {}; private String[] m_dependsOnMethods = {}; - private String m_description = ""; + private @Nullable String m_description = ""; private int m_priority; private long m_timeOut = 0; @@ -38,7 +39,7 @@ public void setGroups(String[] groups) { } @Override - public String getDescription() { + public @Nullable String getDescription() { return m_description; } @@ -58,7 +59,7 @@ public String[] getDependsOnMethods() { } @Override - public void setDescription(String description) { + public void setDescription(@Nullable String description) { m_description = description; } diff --git a/testng-core/src/main/java/org/testng/internal/invokers/ClassBasedParallelWorker.java b/testng-core/src/main/java/org/testng/internal/invokers/ClassBasedParallelWorker.java index 427b86686e..d53f28b4f0 100644 --- a/testng-core/src/main/java/org/testng/internal/invokers/ClassBasedParallelWorker.java +++ b/testng-core/src/main/java/org/testng/internal/invokers/ClassBasedParallelWorker.java @@ -9,6 +9,7 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import org.testng.IMethodInstance; import org.testng.ITestNGMethod; import org.testng.internal.MethodInstance; @@ -112,7 +113,7 @@ private List methodsToMultipleMethodInstances(ITestNGMethod... m } private static boolean isSequential( - org.testng.annotations.ITestAnnotation test, XmlTest xmlTest) { + org.testng.annotations.@Nullable ITestAnnotation test, XmlTest xmlTest) { return test != null && test.getSingleThreaded() || XmlSuite.ParallelMode.CLASSES.equals(xmlTest.getParallel()); } diff --git a/testng-core/src/main/java/org/testng/internal/invokers/ConfigInvoker.java b/testng-core/src/main/java/org/testng/internal/invokers/ConfigInvoker.java index b3840dc953..9d83ec95cb 100644 --- a/testng-core/src/main/java/org/testng/internal/invokers/ConfigInvoker.java +++ b/testng-core/src/main/java/org/testng/internal/invokers/ConfigInvoker.java @@ -305,7 +305,9 @@ public void invokeConfigurations(ConfigMethodArguments arguments) { handleConfigurationSkip( tm, testResult, - configurationAnnotation, + Objects.requireNonNull( + configurationAnnotation, + "a configuration method always carries a @Before/@After annotation"), arguments.getTestMethod(), arguments.getInstance(), arguments.getSuite());