diff --git a/build-logic/build-parameters/build.gradle.kts b/build-logic/build-parameters/build.gradle.kts new file mode 100644 index 0000000000..90490ce5b1 --- /dev/null +++ b/build-logic/build-parameters/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("org.gradlex.build-parameters") version "1.4.3" +} + +buildParameters { + pluginId("testng.build-parameters") + bool("enableCheckerframework") { + defaultValue.set(false) + description.set("Run CheckerFramework (nullness) verifications") + } + bool("enableErrorprone") { + defaultValue.set(true) + description.set("Enable ErrorProne verifications") + } +} diff --git a/build-logic/code-quality/build.gradle.kts b/build-logic/code-quality/build.gradle.kts index a83857d4ef..76030e00c5 100644 --- a/build-logic/code-quality/build.gradle.kts +++ b/build-logic/code-quality/build.gradle.kts @@ -11,6 +11,8 @@ repositories { dependencies { implementation("org.sonarqube:org.sonarqube.gradle.plugin:2.8") implementation("com.github.autostyle:autostyle-plugin-gradle:3.1") + implementation("org.checkerframework:checkerframework-gradle-plugin:0.6.29") + implementation("net.ltgt.gradle:gradle-errorprone-plugin:3.1.0") } tasks.withType().configureEach { diff --git a/build-logic/code-quality/src/main/kotlin/testng.checkerframework.gradle.kts b/build-logic/code-quality/src/main/kotlin/testng.checkerframework.gradle.kts new file mode 100644 index 0000000000..f9a1994e19 --- /dev/null +++ b/build-logic/code-quality/src/main/kotlin/testng.checkerframework.gradle.kts @@ -0,0 +1,25 @@ +import org.checkerframework.gradle.plugin.CheckerFrameworkExtension + +plugins { + id("org.checkerframework") +} + +dependencies { + checkerFramework("org.checkerframework:checker:3.36.0") +} + +configure { + checkers = listOf( + "org.checkerframework.checker.nullness.NullnessChecker", + "org.checkerframework.checker.optional.OptionalChecker" + ) +} + +tasks.withType().configureEach { + // Don't run the checker on generated code. + if (name.startsWith("compileMainGenerated")) { + checkerFramework { + skipCheckerFramework = true + } + } +} diff --git a/build-logic/code-quality/src/main/kotlin/testng.errorprone.gradle.kts b/build-logic/code-quality/src/main/kotlin/testng.errorprone.gradle.kts new file mode 100644 index 0000000000..16c178029c --- /dev/null +++ b/build-logic/code-quality/src/main/kotlin/testng.errorprone.gradle.kts @@ -0,0 +1,13 @@ +import net.ltgt.gradle.errorprone.errorprone + +plugins { + id("net.ltgt.errorprone") +} + +dependencies { + errorprone("com.google.errorprone:error_prone_core:2.20.0") +} + +tasks.withType().configureEach { + options.errorprone.disableWarningsInGeneratedCode.set(true) +} diff --git a/build-logic/jvm/src/main/kotlin/testng.java.gradle.kts b/build-logic/jvm/src/main/kotlin/testng.java.gradle.kts index a2717c9f93..b8f3586f41 100644 --- a/build-logic/jvm/src/main/kotlin/testng.java.gradle.kts +++ b/build-logic/jvm/src/main/kotlin/testng.java.gradle.kts @@ -2,6 +2,7 @@ plugins { `java-base` id("testng.versioning") id("testng.style") + // id("testng.build-parameters") // Plugin [id: 'testng.build-parameters'] was not found id("testng.repositories") // Improves Gradle Test logging // See https://github.com/vlsi/vlsi-release-plugins/tree/master/plugins/gradle-extensions-plugin @@ -13,6 +14,13 @@ java { targetCompatibility = JavaVersion.VERSION_11 } +if (true /*buildParameters.enableCheckerframework*/) { + apply(plugin = "testng.checkerframework") +} +if (true /*buildParameters.enableErrorprone*/) { + apply(plugin = "testng.errorprone") +} + tasks.withType().configureEach { inputs.property("java.version", System.getProperty("java.version")) inputs.property("java.vendor", System.getProperty("java.vendor")) diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts index ee7e4ab97b..b4e114e7c5 100644 --- a/build-logic/settings.gradle.kts +++ b/build-logic/settings.gradle.kts @@ -7,6 +7,7 @@ dependencyResolutionManagement { rootProject.name = "build-logic" include(":basics") +include(":build-parameters") include(":code-quality") include(":jvm") include(":publishing") diff --git a/testng-asserts/src/main/java/org/testng/Assert.java b/testng-asserts/src/main/java/org/testng/Assert.java index 996196c31a..2502a81cef 100644 --- a/testng-asserts/src/main/java/org/testng/Assert.java +++ b/testng-asserts/src/main/java/org/testng/Assert.java @@ -17,6 +17,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.collections.Lists; /** @@ -52,7 +53,7 @@ protected Assert() { * @param condition the condition to evaluate * @param message the assertion error message */ - public static void assertTrue(boolean condition, String message) { + public static void assertTrue(boolean condition, @Nullable String message) { if (!condition) { failNotEquals(condition, Boolean.TRUE, message); } @@ -74,7 +75,7 @@ public static void assertTrue(boolean condition) { * @param condition the condition to evaluate * @param message the assertion error message */ - public static void assertFalse(boolean condition, String message) { + public static void assertFalse(boolean condition, @Nullable String message) { if (condition) { failNotEquals(condition, Boolean.FALSE, message); // TESTNG-81 } @@ -107,7 +108,8 @@ public static void fail(String message, Throwable realCause) { * * @param message the assertion error message */ - public static void fail(String message) { + // See https://github.com/typetools/checker-framework/issues/2076 + public static /* @ThrowsException */ void fail(@Nullable String message) { throw new AssertionError(message); } @@ -124,7 +126,7 @@ public static void fail() { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Object actual, Object expected, String message) { + public static void assertEquals(Object actual, Object expected, @Nullable String message) { if (expected != null && expected.getClass().isArray()) { assertArrayEquals(actual, expected, message); return; @@ -144,21 +146,23 @@ private static boolean areEqual(Object actual, Object expected) { * consideration hence comparing them by reference. Intended to be called directly to test * equality of collections content. */ - private static void assertEqualsImpl(Object actual, Object expected, String message) { + private static void assertEqualsImpl( + @Nullable Object actual, @Nullable Object expected, @Nullable String message) { boolean equal = areEqualImpl(actual, expected); if (!equal) { failNotEquals(actual, expected, message); } } - private static void assertNotEqualsImpl(Object actual, Object expected, String message) { + private static void assertNotEqualsImpl( + @Nullable Object actual, @Nullable Object expected, @Nullable String message) { boolean notEqual = areNotEqualImpl(actual, expected); if (!notEqual) { failEquals(actual, expected, message); } } - private static boolean areNotEqualImpl(Object actual, Object expected) { + private static boolean areNotEqualImpl(@Nullable Object actual, @Nullable Object expected) { if (expected == null) { return actual != null; } @@ -169,7 +173,7 @@ private static boolean areNotEqualImpl(Object actual, Object expected) { return !expected.equals(actual); } - private static boolean areEqualImpl(Object actual, Object expected) { + private static boolean areEqualImpl(@Nullable Object actual, @Nullable Object expected) { if ((expected == null) && (actual == null)) { return true; } @@ -181,7 +185,8 @@ private static boolean areEqualImpl(Object actual, Object expected) { } /** returns not equal reason or null if equal */ - private static String getArrayNotEqualReason(Object actual, Object expected) { + private static @Nullable String getArrayNotEqualReason( + @Nullable Object actual, @Nullable Object expected) { if (Objects.equals(actual, expected)) { return null; } @@ -212,15 +217,17 @@ private static boolean areArraysEqual(Object actual, Object expected) { return getArrayNotEqualReason(actual, expected) == null; } - private static void assertArrayEquals(Object actual, Object expected, String message) { - String reason = getArrayNotEqualReason(actual, expected); + private static void assertArrayEquals( + @Nullable Object actual, @Nullable Object expected, @Nullable String message) { + @Nullable String reason = getArrayNotEqualReason(actual, expected); if (null != reason) { failNotEquals(actual, expected, message == null ? "" : message + " (" + message + ")"); } } - private static void assertArrayNotEquals(Object actual, Object expected, String message) { - String reason = getArrayNotEqualReason(actual, expected); + private static void assertArrayNotEquals( + @Nullable Object actual, @Nullable Object expected, @Nullable String message) { + @Nullable String reason = getArrayNotEqualReason(actual, expected); if (null == reason) { failEquals(actual, expected, message); } @@ -652,7 +659,7 @@ public static void assertEquals(Object actual, Object expected) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(String actual, String expected, String message) { + public static void assertEquals(String actual, String expected, @Nullable String message) { assertEquals((Object) actual, (Object) expected, message); } @@ -693,7 +700,8 @@ private static boolean areEqual(double actual, double expected, double delta) { * @param delta the absolute tolerable difference between the actual and expected values * @param message the assertion error message */ - public static void assertEquals(double actual, double expected, double delta, String message) { + public static void assertEquals( + double actual, double expected, double delta, @Nullable String message) { if (!areEqual(actual, expected, delta)) { failNotEquals(actual, expected, message); } @@ -719,7 +727,7 @@ public static void assertEquals(double actual, double expected, double delta) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(double actual, double expected, String message) { + public static void assertEquals(double actual, double expected, @Nullable String message) { if (Double.isNaN(expected)) { if (!Double.isNaN(actual)) { failNotEquals(actual, expected, message); @@ -761,7 +769,7 @@ public static void assertEquals(double actual, Double expected, String message) * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Double actual, Double expected, String message) { + public static void assertEquals(Double actual, Double expected, @Nullable String message) { assertEquals(actual, (Object) expected, message); } @@ -832,7 +840,8 @@ private static boolean areEqual(float actual, float expected, float delta) { * @param delta the absolute tolerable difference between the actual and expected values * @param message the assertion error message */ - public static void assertEquals(float actual, float expected, float delta, String message) { + public static void assertEquals( + float actual, float expected, float delta, @Nullable String message) { if (!areEqual(actual, expected, delta)) { failNotEquals(actual, expected, message); } @@ -858,7 +867,7 @@ public static void assertEquals(float actual, float expected, float delta) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(float actual, float expected, String message) { + public static void assertEquals(float actual, float expected, @Nullable String message) { if (Float.isNaN(expected)) { if (!Float.isNaN(actual)) { failNotEquals(actual, expected, message); @@ -900,7 +909,7 @@ public static void assertEquals(float actual, Float expected, String message) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Float actual, Float expected, String message) { + public static void assertEquals(Float actual, Float expected, @Nullable String message) { assertEquals(actual, (Object) expected, message); } @@ -952,7 +961,7 @@ public static void assertEquals(Float actual, Float expected) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(long actual, long expected, String message) { + public static void assertEquals(long actual, long expected, @Nullable String message) { assertEquals(Long.valueOf(actual), Long.valueOf(expected), message); } @@ -976,7 +985,7 @@ public static void assertEquals(Long actual, long expected, String message) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Long actual, Long expected, String message) { + public static void assertEquals(Long actual, Long expected, @Nullable String message) { assertEquals(actual, (Object) expected, message); } @@ -1027,7 +1036,7 @@ public static void assertEquals(Long actual, Long expected) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(boolean actual, boolean expected, String message) { + public static void assertEquals(boolean actual, boolean expected, @Nullable String message) { assertEquals(Boolean.valueOf(actual), Boolean.valueOf(expected), message); } @@ -1063,7 +1072,7 @@ public static void assertEquals(boolean actual, Boolean expected, String message * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Boolean actual, Boolean expected, String message) { + public static void assertEquals(Boolean actual, Boolean expected, @Nullable String message) { assertEquals(actual, (Object) expected, message); } @@ -1115,7 +1124,7 @@ public static void assertEquals(boolean actual, Boolean expected) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(byte actual, byte expected, String message) { + public static void assertEquals(byte actual, byte expected, @Nullable String message) { assertEquals(Byte.valueOf(actual), Byte.valueOf(expected), message); } @@ -1151,7 +1160,7 @@ public static void assertEquals(byte actual, Byte expected, String message) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Byte actual, Byte expected, String message) { + public static void assertEquals(Byte actual, Byte expected, @Nullable String message) { assertEquals(actual, (Object) expected, message); } @@ -1203,7 +1212,7 @@ public static void assertEquals(byte actual, Byte expected) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(char actual, char expected, String message) { + public static void assertEquals(char actual, char expected, @Nullable String message) { assertEquals(Character.valueOf(actual), Character.valueOf(expected), message); } @@ -1239,7 +1248,7 @@ public static void assertEquals(char actual, Character expected, String message) * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Character actual, Character expected, String message) { + public static void assertEquals(Character actual, Character expected, @Nullable String message) { assertEquals(actual, (Object) expected, message); } @@ -1291,7 +1300,7 @@ public static void assertEquals(Character actual, Character expected) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(short actual, short expected, String message) { + public static void assertEquals(short actual, short expected, @Nullable String message) { assertEquals(Short.valueOf(actual), Short.valueOf(expected), message); } @@ -1327,7 +1336,7 @@ public static void assertEquals(short actual, Short expected, String message) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Short actual, Short expected, String message) { + public static void assertEquals(Short actual, Short expected, @Nullable String message) { assertEquals(actual, (Object) expected, message); } @@ -1379,7 +1388,7 @@ public static void assertEquals(Short actual, Short expected) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(int actual, int expected, String message) { + public static void assertEquals(int actual, int expected, @Nullable String message) { assertEquals(Integer.valueOf(actual), Integer.valueOf(expected), message); } @@ -1415,7 +1424,7 @@ public static void assertEquals(int actual, Integer expected, String message) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Integer actual, Integer expected, String message) { + public static void assertEquals(Integer actual, Integer expected, @Nullable String message) { assertEquals(actual, (Object) expected, message); } @@ -1475,7 +1484,7 @@ public static void assertNotNull(Object object) { * @param object the assertion object * @param message the assertion error message */ - public static void assertNotNull(Object object, String message) { + public static void assertNotNull(Object object, @Nullable String message) { if (object == null) { String formatted = ""; if (message != null) { @@ -1502,7 +1511,7 @@ public static void assertNull(Object object) { * @param object the assertion object * @param message the assertion error message */ - public static void assertNull(Object object, String message) { + public static void assertNull(@Nullable Object object, @Nullable String message) { if (object != null) { failNotSame(object, null, message); } @@ -1516,7 +1525,7 @@ public static void assertNull(Object object, String message) { * @param expected the expected value * @param message the assertion error message */ - public static void assertSame(Object actual, Object expected, String message) { + public static void assertSame(Object actual, Object expected, @Nullable String message) { if (expected == actual) { return; } @@ -1541,7 +1550,7 @@ public static void assertSame(Object actual, Object expected) { * @param expected the expected value * @param message the assertion error message */ - public static void assertNotSame(Object actual, Object expected, String message) { + public static void assertNotSame(Object actual, Object expected, @Nullable String message) { if (expected == actual) { failSame(actual, expected, message); } @@ -1558,7 +1567,7 @@ public static void assertNotSame(Object actual, Object expected) { assertNotSame(actual, expected, null); } - private static void failSame(Object actual, Object expected, String message) { + private static void failSame(Object actual, Object expected, @Nullable String message) { String formatted = ""; if (message != null) { formatted = message + " "; @@ -1566,7 +1575,8 @@ private static void failSame(Object actual, Object expected, String message) { fail(formatted + ASSERT_LEFT2 + expected + ASSERT_MIDDLE + actual + ASSERT_RIGHT); } - private static void failNotSame(Object actual, Object expected, String message) { + private static void failNotSame( + @Nullable Object actual, @Nullable Object expected, @Nullable String message) { String formatted = ""; if (message != null) { formatted = message + " "; @@ -1574,15 +1584,21 @@ private static void failNotSame(Object actual, Object expected, String message) fail(formatted + ASSERT_EQUAL_LEFT + expected + ASSERT_MIDDLE + actual + ASSERT_RIGHT); } - private static void failNotEquals(Object actual, Object expected, String message) { + private static void failNotEquals( + @Nullable Object actual, @Nullable Object expected, @Nullable String message) { fail(format(actual, expected, message, true)); } - private static void failEquals(Object actual, Object expected, String message) { + private static void failEquals( + @Nullable Object actual, @Nullable Object expected, @Nullable String message) { fail(format(actual, expected, message, false)); } - static String format(Object actual, Object expected, String message, boolean isAssertEquals) { + static String format( + @Nullable Object actual, + @Nullable Object expected, + @Nullable String message, + boolean isAssertEquals) { String formatted = ""; if (null != message) { formatted = message + " "; @@ -1602,7 +1618,8 @@ static String format(Object actual, Object expected, String message, boolean isA * @param actual the actual value * @param expected the expected value */ - public static void assertEquals(Collection actual, Collection expected) { + public static void assertEquals( + @Nullable Collection<@Nullable ?> actual, @Nullable Collection<@Nullable ?> expected) { assertEquals(actual, expected, null); } @@ -1614,7 +1631,10 @@ public static void assertEquals(Collection actual, Collection expected) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Collection actual, Collection expected, String message) { + public static void assertEquals( + @Nullable Collection<@Nullable ?> actual, + @Nullable Collection<@Nullable ?> expected, + @Nullable String message) { if (actual == expected) { // We don't use Objects.equals here because order is checked return; } @@ -1625,6 +1645,7 @@ public static void assertEquals(Collection actual, Collection expected, St } else { fail("Collections not equal: expected: " + expected + " and actual: " + actual); } + return; } assertEquals( @@ -1632,13 +1653,13 @@ public static void assertEquals(Collection actual, Collection expected, St expected.size(), (message == null ? "" : message + ": ") + "lists don't have the same size"); - Iterator actIt = actual.iterator(); - Iterator expIt = expected.iterator(); + Iterator<@Nullable ?> actIt = actual.iterator(); + Iterator<@Nullable ?> expIt = expected.iterator(); int i = -1; while (actIt.hasNext() && expIt.hasNext()) { i++; - Object e = expIt.next(); - Object a = actIt.next(); + @Nullable Object e = expIt.next(); + @Nullable Object a = actIt.next(); String explanation = "Lists differ at element [" + i + "]: " + e + " != " + a; String errorMessage = message == null ? explanation : message + ": " + explanation; assertEqualsImpl(a, e, errorMessage); @@ -1653,7 +1674,7 @@ public static void assertEquals(Collection actual, Collection expected, St * @param actual the actual value * @param expected the expected value */ - public static void assertEquals(Iterator actual, Iterator expected) { + public static void assertEquals(@Nullable Iterator actual, @Nullable Iterator expected) { assertEquals(actual, expected, null); } @@ -1666,7 +1687,10 @@ public static void assertEquals(Iterator actual, Iterator expected) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Iterator actual, Iterator expected, String message) { + public static void assertEquals( + @Nullable Iterator<@Nullable ?> actual, + @Nullable Iterator<@Nullable ?> expected, + @Nullable String message) { if (actual == expected) { // We don't use Objects.equals here because order is checked return; } @@ -1676,14 +1700,15 @@ public static void assertEquals(Iterator actual, Iterator expected, String ? message : "Iterators not equal: expected: " + expected + " and actual: " + actual; fail(msg); + return; } int i = -1; while (actual.hasNext() && expected.hasNext()) { i++; - Object e = expected.next(); - Object a = actual.next(); + @Nullable Object e = expected.next(); + @Nullable Object a = actual.next(); String explanation = "Iterators differ at element [" + i + "]: " + e + " != " + a; String errorMessage = message == null ? explanation : message + ": " + explanation; @@ -1711,7 +1736,8 @@ public static void assertEquals(Iterator actual, Iterator expected, String * @param actual the actual value * @param expected the expected value */ - public static void assertEquals(Iterable actual, Iterable expected) { + public static void assertEquals( + @Nullable Iterable<@Nullable ?> actual, @Nullable Iterable<@Nullable ?> expected) { assertEquals(actual, expected, null); } @@ -1723,7 +1749,10 @@ public static void assertEquals(Iterable actual, Iterable expected) { * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Iterable actual, Iterable expected, String message) { + public static void assertEquals( + @Nullable Iterable<@Nullable ?> actual, + @Nullable Iterable<@Nullable ?> expected, + @Nullable String message) { if (actual == expected) { // We don't use Objects.equals here because order is checked return; } @@ -1734,10 +1763,11 @@ public static void assertEquals(Iterable actual, Iterable expected, String } else { fail("Iterables not equal: expected: " + expected + " and actual: " + actual); } + return; } - Iterator actIt = actual.iterator(); - Iterator expIt = expected.iterator(); + Iterator<@Nullable ?> actIt = actual.iterator(); + Iterator<@Nullable ?> expIt = expected.iterator(); assertEquals(actIt, expIt, message); } @@ -1750,12 +1780,15 @@ public static void assertEquals(Iterable actual, Iterable expected, String * @param expected the expected value * @param message the assertion error message */ - public static void assertEquals(Object[] actual, Object[] expected, String message) { + public static void assertEquals( + @Nullable Object @Nullable [] actual, + @Nullable Object @Nullable [] expected, + @Nullable String message) { if (Arrays.equals(actual, expected)) { return; } - if ((actual == null && expected != null) || (actual != null && expected == null)) { + if (actual == null || expected == null) { if (message != null) { fail(message); } else { @@ -1765,6 +1798,7 @@ public static void assertEquals(Object[] actual, Object[] expected, String messa + " and actual: " + Arrays.toString(actual)); } + return; } if (actual.length != expected.length) { failAssertNoEqual( @@ -1772,14 +1806,14 @@ public static void assertEquals(Object[] actual, Object[] expected, String messa } for (int i = 0; i < expected.length; i++) { - Object e = expected[i]; - Object a = actual[i]; + @Nullable Object e = expected[i]; + @Nullable Object a = actual[i]; String explanation = "Arrays differ at element [" + i + "]: " + e + " != " + a; String errorMessage = message == null ? explanation : message + ": " + explanation; if (a == null && e == null) { continue; } - if ((a == null && e != null) || (a != null && e == null)) { + if (a == null || e == null) { failNotEquals(a, e, message); } // Compare by value for multi-dimensional array. @@ -1801,7 +1835,10 @@ public static void assertEquals(Object[] actual, Object[] expected, String messa * @param expected the expected value * @param message the assertion error message */ - public static void assertEqualsNoOrder(Object[] actual, Object[] expected, String message) { + public static void assertEqualsNoOrder( + @Nullable Object @Nullable [] actual, + @Nullable Object @Nullable [] expected, + @Nullable String message) { if (actual == expected) { // We don't use Arrays.equals here because order is not checked return; } @@ -1813,14 +1850,16 @@ public static void assertEqualsNoOrder(Object[] actual, Object[] expected, Strin + " and actual: " + Arrays.toString(actual), message); + return; } if (actual.length != expected.length) { failAssertNoEqual( "Arrays do not have the same size:" + actual.length + " != " + expected.length, message); + return; } - List actualCollection = Lists.newArrayList(actual); + List<@Nullable Object> actualCollection = Lists.newArrayList(actual); for (Object o : expected) { actualCollection.remove(o); } @@ -1843,11 +1882,12 @@ public static void assertEqualsNoOrder(Object[] actual, Object[] expected, Strin * @param message the assertion error message */ public static void assertEqualsNoOrder( - Collection actual, Collection expected, String message) { + Collection<@Nullable ?> actual, Collection<@Nullable ?> expected, @Nullable String message) { if (actual.size() != expected.size()) { failAssertNoEqual( "Collections do not have the same size: " + actual.size() + " != " + expected.size(), message); + return; } List actualCollection = Lists.newArrayList(actual); @@ -1866,7 +1906,8 @@ public static void assertEqualsNoOrder( * @param expected the expected value * @param message the assertion error message */ - public static void assertEqualsNoOrder(Iterator actual, Iterator expected, String message) { + public static void assertEqualsNoOrder( + Iterator<@Nullable ?> actual, Iterator<@Nullable ?> expected, @Nullable String message) { List actualCollection = Lists.newArrayList(actual); List expectedCollection = Lists.newArrayList(expected); @@ -1877,6 +1918,7 @@ public static void assertEqualsNoOrder(Iterator actual, Iterator expected, + " != " + expectedCollection.size(), message); + return; } actualCollection.removeAll(expectedCollection); @@ -1890,7 +1932,7 @@ public static void assertEqualsNoOrder(Iterator actual, Iterator expected, } } - private static String toString(Iterator iterator) { + private static @Nullable String toString(@Nullable Iterator iterator) { if (iterator == null) { return null; } @@ -1900,7 +1942,7 @@ private static String toString(Iterator iterator) { .collect(Collectors.joining(", ")); } - private static void failAssertNoEqual(String defaultMessage, String message) { + private static void failAssertNoEqual(String defaultMessage, @Nullable String message) { if (message != null) { fail(message); } else { @@ -1915,7 +1957,8 @@ private static void failAssertNoEqual(String defaultMessage, String message) { * @param actual the actual value * @param expected the expected value */ - public static void assertEquals(Object[] actual, Object[] expected) { + public static void assertEquals( + @Nullable Object @Nullable [] actual, @Nullable Object @Nullable [] expected) { assertEquals(actual, expected, null); } @@ -1928,7 +1971,8 @@ public static void assertEquals(Object[] actual, Object[] expected) { * @param actual the actual value * @param expected the expected value */ - public static void assertEqualsNoOrder(Object[] actual, Object[] expected) { + public static void assertEqualsNoOrder( + @Nullable Object @Nullable [] actual, @Nullable Object @Nullable [] expected) { assertEqualsNoOrder(actual, expected, null); } @@ -1939,7 +1983,8 @@ public static void assertEqualsNoOrder(Object[] actual, Object[] expected) { * @param actual the actual value * @param expected the expected value */ - public static void assertEqualsNoOrder(Collection actual, Collection expected) { + public static void assertEqualsNoOrder( + Collection<@Nullable ?> actual, Collection<@Nullable ?> expected) { assertEqualsNoOrder(actual, expected, null); } @@ -1950,7 +1995,8 @@ public static void assertEqualsNoOrder(Collection actual, Collection expec * @param actual the actual value * @param expected the expected value */ - public static void assertEqualsNoOrder(Iterator actual, Iterator expected) { + public static void assertEqualsNoOrder( + Iterator<@Nullable ?> actual, Iterator<@Nullable ?> expected) { assertEqualsNoOrder(actual, expected, null); } @@ -1960,12 +2006,13 @@ public static void assertEqualsNoOrder(Iterator actual, Iterator expected) * @param actual The actual value * @param expected The expected value */ - public static void assertEquals(Set actual, Set expected) { + public static void assertEquals(@Nullable Set actual, @Nullable Set expected) { assertEquals(actual, expected, null); } /** returns not equal reason or null if equal */ - private static String getNotEqualReason(Collection actual, Collection expected) { + private static @Nullable String getNotEqualReason( + @Nullable Collection actual, @Nullable Collection expected) { if (actual == expected) { // We don't use Arrays.equals here because order is checked return null; } @@ -1982,7 +2029,8 @@ private static String getNotEqualReason(Collection actual, Collection expe return getNotEqualReason(actual.iterator(), expected.iterator()); } - private static String getNotEqualReason(Iterator actual, Iterator expected) { + private static @Nullable String getNotEqualReason( + @Nullable Iterator<@Nullable ?> actual, @Nullable Iterator<@Nullable ?> expected) { if (actual == expected) { // We don't use Arrays.equals here because order is checked return null; } @@ -2006,7 +2054,8 @@ private static String getNotEqualReason(Iterator actual, Iterator expected return null; } - private static String getNotEqualReason(Set actual, Set expected) { + private static @Nullable String getNotEqualReason( + @Nullable Set<@Nullable ?> actual, @Nullable Set<@Nullable ?> expected) { if (actual == expected) { return null; } @@ -2029,8 +2078,11 @@ private static String getNotEqualReason(Set actual, Set expected) { * @param expected The expected value * @param message The message */ - public static void assertEquals(Set actual, Set expected, String message) { - String notEqualReason = getNotEqualReason(actual, expected); + public static void assertEquals( + @Nullable Set<@Nullable ?> actual, + @Nullable Set<@Nullable ?> expected, + @Nullable String message) { + @Nullable String notEqualReason = getNotEqualReason(actual, expected); if (null != notEqualReason) { // Keep the back compatible if (message == null) { @@ -2042,7 +2094,8 @@ public static void assertEquals(Set actual, Set expected, String message) } /** returns not equal deep reason or null if equal */ - private static String getNotEqualDeepReason(Set actual, Set expected) { + private static @Nullable String getNotEqualDeepReason( + @Nullable Set<@Nullable ?> actual, @Nullable Set<@Nullable ?> expected) { if (Objects.equals(actual, expected)) { return null; } @@ -2062,7 +2115,7 @@ private static String getNotEqualDeepReason(Set actual, Set expected) { Object expectedValue = expectedIterator.next(); Object value = actualIterator.next(); if (expectedValue.getClass().isArray()) { - String arrayNotEqualReason = getArrayNotEqualReason(value, expectedValue); + @Nullable String arrayNotEqualReason = getArrayNotEqualReason(value, expectedValue); if (arrayNotEqualReason != null) { return arrayNotEqualReason; } @@ -2075,8 +2128,11 @@ private static String getNotEqualDeepReason(Set actual, Set expected) { return null; } - public static void assertEqualsDeep(Set actual, Set expected, String message) { - String notEqualDeepReason = getNotEqualDeepReason(actual, expected); + public static void assertEqualsDeep( + @Nullable Set<@Nullable ?> actual, + @Nullable Set<@Nullable ?> expected, + @Nullable String message) { + @Nullable String notEqualDeepReason = getNotEqualDeepReason(actual, expected); if (notEqualDeepReason != null) { if (message == null) { fail(notEqualDeepReason); @@ -2086,11 +2142,12 @@ public static void assertEqualsDeep(Set actual, Set expected, String messa } } - public static void assertEquals(Map actual, Map expected) { + public static void assertEquals(@Nullable Map actual, @Nullable Map expected) { assertEquals(actual, expected, null); } - private static String getNotEqualReason(Map actual, Map expected) { + private static @Nullable String getNotEqualReason( + @Nullable Map actual, @Nullable Map expected) { if (Objects.equals(actual, expected)) { return null; } @@ -2128,8 +2185,9 @@ private static String getNotEqualReason(Map actual, Map expected) { * @param expected The expected value * @param message The message */ - public static void assertEquals(Map actual, Map expected, String message) { - String notEqualReason = getNotEqualReason(actual, expected); + public static void assertEquals( + @Nullable Map actual, @Nullable Map expected, @Nullable String message) { + @Nullable String notEqualReason = getNotEqualReason(actual, expected); if (notEqualReason != null) { if (message == null) { fail(notEqualReason); @@ -2139,12 +2197,13 @@ public static void assertEquals(Map actual, Map expected, String mes } } - public static void assertEqualsDeep(Map actual, Map expected) { + public static void assertEqualsDeep(@Nullable Map actual, @Nullable Map expected) { assertEqualsDeep(actual, expected, null); } /** returns not equal deep reason or null if equal */ - private static String getNotEqualDeepReason(Map actual, Map expected) { + private static @Nullable String getNotEqualDeepReason( + @Nullable Map actual, @Nullable Map expected) { if (Objects.equals(actual, expected)) { return null; } @@ -2178,8 +2237,9 @@ private static String getNotEqualDeepReason(Map actual, Map expected return null; } - public static void assertEqualsDeep(Map actual, Map expected, String message) { - String notEqualDeepReason = getNotEqualDeepReason(actual, expected); + public static void assertEqualsDeep( + @Nullable Map actual, @Nullable Map expected, @Nullable String message) { + @Nullable String notEqualDeepReason = getNotEqualDeepReason(actual, expected); if (notEqualDeepReason != null) { if (message == null) { fail(notEqualDeepReason); @@ -2193,7 +2253,8 @@ public static void assertEqualsDeep(Map actual, Map expected, String // assertNotEquals // - public static void assertNotEquals(Object actual, Object expected, String message) { + public static void assertNotEquals( + @Nullable Object actual, @Nullable Object expected, @Nullable String message) { if (expected != null && expected.getClass().isArray()) { assertArrayNotEquals(actual, expected, message); return; diff --git a/testng-asserts/src/main/java/org/testng/FileAssert.java b/testng-asserts/src/main/java/org/testng/FileAssert.java index 5c80dd333e..d6f91af8fe 100644 --- a/testng-asserts/src/main/java/org/testng/FileAssert.java +++ b/testng-asserts/src/main/java/org/testng/FileAssert.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.IOException; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Assertion tool for File centric assertions. Conceptually, this is an extension of {@link Assert}. @@ -25,7 +26,7 @@ private FileAssert() { * @param tstvalue the file to evaluate * @param message the assertion error message */ - public static void assertDirectory(File tstvalue, String message) { + public static void assertDirectory(File tstvalue, @Nullable String message) { boolean condition = false; try { condition = tstvalue.isDirectory(); @@ -77,7 +78,7 @@ public static void assertFile(File tstvalue) { * @param expected the expected value * @param message the assertion error message */ - public static void assertLength(File tstvalue, long expected, String message) { + public static void assertLength(File tstvalue, long expected, @Nullable String message) { long actual = -1L; try { actual = tstvalue.isDirectory() ? tstvalue.list().length : tstvalue.length(); @@ -107,7 +108,7 @@ public static void assertLength(File tstvalue, long expected) { * @param expected the expected value * @param message the assertion error message */ - public static void assertMinLength(File tstvalue, long expected, String message) { + public static void assertMinLength(File tstvalue, long expected, @Nullable String message) { long actual = -1L; try { actual = tstvalue.isDirectory() ? tstvalue.list().length : tstvalue.length(); @@ -138,7 +139,7 @@ public static void assertMinLength(File tstvalue, long expected) { * @param expected The expected max length * @param message the assertion error message */ - public static void assertMaxLength(File tstvalue, long expected, String message) { + public static void assertMaxLength(File tstvalue, long expected, @Nullable String message) { long actual = -1L; try { actual = tstvalue.isDirectory() ? tstvalue.list().length : tstvalue.length(); @@ -167,7 +168,7 @@ public static void assertMaxLength(File tstvalue, long expected) { * @param tstvalue the file to evaluate * @param message the assertion error message */ - public static void assertReadable(File tstvalue, String message) { + public static void assertReadable(File tstvalue, @Nullable String message) { boolean condition = false; try { condition = tstvalue.canRead(); @@ -221,7 +222,7 @@ public static void assertWriteable(File tstvalue) { * @param tstvalue the file to evaluate * @param message the assertion error message */ - public static void assertReadWrite(File tstvalue, String message) { + public static void assertReadWrite(File tstvalue, @Nullable String message) { boolean condition = false; try { condition = tstvalue.canRead() && tstvalue.canWrite(); @@ -259,7 +260,7 @@ public static void fail(String message, Throwable realCause) { * * @param message the assertion error message */ - public static void fail(String message) { + public static void fail(@Nullable String message) { throw new AssertionError(message); } @@ -269,7 +270,8 @@ public static void fail() { } /** Formats failure for file assertions. */ - private static void failFile(File path, String actual, String expected, String message) { + private static void failFile( + File path, String actual, @Nullable String expected, @Nullable String message) { String formatted = ""; if (message != null) { formatted = message + " "; @@ -291,7 +293,7 @@ private static void failFile(File path, String actual, String expected, String m * @param message */ private static void failSecurity( - Exception e, File path, String actual, String expected, String message) { + Exception e, File path, String actual, String expected, @Nullable String message) { String formatted = ""; if (message != null) { formatted = message + " "; diff --git a/testng-asserts/src/main/java/org/testng/asserts/Assertion.java b/testng-asserts/src/main/java/org/testng/asserts/Assertion.java index a3778de3f5..6c2afff713 100644 --- a/testng-asserts/src/main/java/org/testng/asserts/Assertion.java +++ b/testng-asserts/src/main/java/org/testng/asserts/Assertion.java @@ -3,6 +3,7 @@ import java.util.Collection; import java.util.Map; import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; /** An assert class with various hooks allowing its behavior to be modified by subclasses. */ public class Assertion implements IAssertLifecycle { @@ -41,19 +42,19 @@ public void onBeforeAssert(IAssert assertCommand) {} public void onAfterAssert(IAssert assertCommand) {} private abstract static class SimpleAssert implements IAssert { - private final T actual; - private final T expected; - private final String m_message; + private final @Nullable T actual; + private final @Nullable T expected; + private final @Nullable String m_message; public SimpleAssert(String message) { this(null, null, message); } - public SimpleAssert(T actual, T expected) { + public SimpleAssert(@Nullable T actual, @Nullable T expected) { this(actual, expected, null); } - public SimpleAssert(T actual, T expected, String message) { + public SimpleAssert(@Nullable T actual, @Nullable T expected, String message) { this.actual = actual; this.expected = expected; m_message = message; diff --git a/testng-asserts/src/main/java/org/testng/asserts/IAssert.java b/testng-asserts/src/main/java/org/testng/asserts/IAssert.java index f6b3d42731..43ecd53c95 100644 --- a/testng-asserts/src/main/java/org/testng/asserts/IAssert.java +++ b/testng-asserts/src/main/java/org/testng/asserts/IAssert.java @@ -1,11 +1,16 @@ package org.testng.asserts; +import org.checkerframework.checker.nullness.qual.Nullable; + public interface IAssert { + @Nullable String getMessage(); void doAssert(); + @Nullable T getActual(); + @Nullable T getExpected(); } diff --git a/testng-asserts/src/main/java/org/testng/asserts/SoftAssert.java b/testng-asserts/src/main/java/org/testng/asserts/SoftAssert.java index 3746eb07e2..3d71367200 100644 --- a/testng-asserts/src/main/java/org/testng/asserts/SoftAssert.java +++ b/testng-asserts/src/main/java/org/testng/asserts/SoftAssert.java @@ -1,6 +1,7 @@ package org.testng.asserts; import java.util.Map; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.collections.Maps; /** @@ -30,7 +31,7 @@ public void assertAll() { assertAll(null); } - public void assertAll(String message) { + public void assertAll(@Nullable String message) { if (!m_errors.isEmpty()) { StringBuilder sb = new StringBuilder(null == message ? DEFAULT_SOFT_ASSERT_MESSAGE : message); boolean first = true; diff --git a/testng-asserts/src/main/java/org/testng/package-info.java b/testng-asserts/src/main/java/org/testng/package-info.java new file mode 100644 index 0000000000..bf86fddb16 --- /dev/null +++ b/testng-asserts/src/main/java/org/testng/package-info.java @@ -0,0 +1,6 @@ +@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.ALL) +package org.testng; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.checkerframework.framework.qual.TypeUseLocation; diff --git a/testng-asserts/testng-asserts-build.gradle.kts b/testng-asserts/testng-asserts-build.gradle.kts index 2e958012d5..7d8a3e137e 100644 --- a/testng-asserts/testng-asserts-build.gradle.kts +++ b/testng-asserts/testng-asserts-build.gradle.kts @@ -6,6 +6,7 @@ dependencies { implementation(projects.testngCollections) { because("Lists.newArrayList") } + implementation("org.checkerframework:checker-qual:3.36.0") testImplementation("org.testng:testng:7.3.0") { because("core depends on assertions and we need testng to test assertions") diff --git a/testng-collections/src/main/java/org/testng/collections/Maps.java b/testng-collections/src/main/java/org/testng/collections/Maps.java index 294f2d2b85..5825759b14 100644 --- a/testng-collections/src/main/java/org/testng/collections/Maps.java +++ b/testng-collections/src/main/java/org/testng/collections/Maps.java @@ -1,5 +1,6 @@ package org.testng.collections; +import com.google.errorprone.annotations.InlineMe; import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; @@ -13,6 +14,8 @@ public static Map newHashMap() { return new HashMap<>(); } + @Deprecated + @InlineMe(replacement = "new Hashtable<>()", imports = "java.util.Hashtable") public static Map newHashtable() { return new Hashtable<>(); } diff --git a/testng-collections/src/main/java/org/testng/collections/MultiMap.java b/testng-collections/src/main/java/org/testng/collections/MultiMap.java index 8d2df157f6..47202f8ce7 100644 --- a/testng-collections/src/main/java/org/testng/collections/MultiMap.java +++ b/testng-collections/src/main/java/org/testng/collections/MultiMap.java @@ -5,6 +5,8 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import org.checkerframework.checker.nullness.qual.KeyFor; +import org.checkerframework.checker.nullness.qual.Nullable; public abstract class MultiMap> { protected final Map m_objects; @@ -47,18 +49,17 @@ public boolean containsKey(K k) { @Override public String toString() { StringBuilder result = new StringBuilder(); - Set indices = keySet(); - for (K i : indices) { - result.append("\n ").append(i).append(" <-- "); - for (Object o : m_objects.get(i)) { - result.append(o).append(" "); + for (Map.Entry entry : m_objects.entrySet()) { + result.append("\n ").append(entry.getKey()).append(" <-- "); + for (V v : entry.getValue()) { + result.append(v).append(" "); } } return result.toString(); } public boolean isEmpty() { - return m_objects.size() == 0; + return m_objects.isEmpty(); } public int size() { @@ -69,11 +70,11 @@ public boolean remove(K key, V value) { return get(key).remove(value); } - public C removeAll(K key) { + public @Nullable C removeAll(K key) { return m_objects.remove(key); } - public Set> entrySet() { + public Set> entrySet() { return m_objects.entrySet(); } diff --git a/testng-collections/src/main/java/org/testng/package-info.java b/testng-collections/src/main/java/org/testng/package-info.java new file mode 100644 index 0000000000..bf86fddb16 --- /dev/null +++ b/testng-collections/src/main/java/org/testng/package-info.java @@ -0,0 +1,6 @@ +@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.ALL) +package org.testng; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.checkerframework.framework.qual.TypeUseLocation; diff --git a/testng-collections/src/main/java/org/testng/util/Strings.java b/testng-collections/src/main/java/org/testng/util/Strings.java index 452f56f29d..b837ee7604 100644 --- a/testng-collections/src/main/java/org/testng/util/Strings.java +++ b/testng-collections/src/main/java/org/testng/util/Strings.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.stream.Collectors; import org.testng.collections.Maps; @@ -12,7 +11,10 @@ private Strings() { } public static boolean isNullOrEmpty(String string) { - return Optional.ofNullable(string).orElse("").trim().isEmpty(); + if (string == null) { + return true; + } + return string.isEmpty(); } public static boolean isNotNullAndNotEmpty(String string) { @@ -52,7 +54,7 @@ public static String escapeHtml(String text) { return result; } - public static String valueOf(Map m) { + public static String valueOf(Map m) { return m.values().stream().map(Object::toString).collect(Collectors.joining(" ")); } diff --git a/testng-collections/testng-collections-build.gradle.kts b/testng-collections/testng-collections-build.gradle.kts index 0438ec3bf1..9433ef384e 100644 --- a/testng-collections/testng-collections-build.gradle.kts +++ b/testng-collections/testng-collections-build.gradle.kts @@ -1,3 +1,8 @@ plugins { id("testng.java-library") } + +dependencies { + implementation("org.checkerframework:checker-qual:3.36.0") + implementation("com.google.errorprone:error_prone_annotations:2.20.0") +} diff --git a/testng-core-api/src/main/java/org/testng/IAttributes.java b/testng-core-api/src/main/java/org/testng/IAttributes.java index 98e925cc9c..7165c61339 100644 --- a/testng-core-api/src/main/java/org/testng/IAttributes.java +++ b/testng-core-api/src/main/java/org/testng/IAttributes.java @@ -5,8 +5,9 @@ /** A trait that is used by all interfaces that lets the user add or remove their own attributes. */ public interface IAttributes { /** + * Returns the attribute with the given name. + * * @param name The name of the attribute to return - * @return The attribute */ Object getAttribute(String name); @@ -18,7 +19,7 @@ public interface IAttributes { */ void setAttribute(String name, Object value); - /** @return all the attributes names. */ + /** Returns all the attributes names. */ Set getAttributeNames(); /** diff --git a/testng-core-api/src/main/java/org/testng/IClass.java b/testng-core-api/src/main/java/org/testng/IClass.java index adf1df9031..66475f43c4 100644 --- a/testng-core-api/src/main/java/org/testng/IClass.java +++ b/testng-core-api/src/main/java/org/testng/IClass.java @@ -6,19 +6,19 @@ /** IClass represents a test class and a collection of its instances. */ public interface IClass { - /** @return this test class name. This is the name of the corresponding Java class. */ + /** Returns this test class name. This is the name of the corresponding Java class. */ String getName(); - /** @return the <test> tag this class was found in. */ + /** Returns the <test> tag this class was found in. */ XmlTest getXmlTest(); - /** @return the *lt;class> tag this class was found in. */ + /** Returns the *lt;class> tag this class was found in. */ XmlClass getXmlClass(); - /** @return its test name if this class implements org.testng.ITest, null otherwise. */ + /** Returns its test name if this class implements org.testng.ITest, null otherwise. */ String getTestName(); - /** @return the Java class corresponding to this IClass. */ + /** Returns the Java class corresponding to this IClass. */ Class getRealClass(); /** diff --git a/testng-core-api/src/main/java/org/testng/IObjectFactory2.java b/testng-core-api/src/main/java/org/testng/IObjectFactory2.java index a690d7117f..b829a3b4d2 100644 --- a/testng-core-api/src/main/java/org/testng/IObjectFactory2.java +++ b/testng-core-api/src/main/java/org/testng/IObjectFactory2.java @@ -18,6 +18,6 @@ public interface IObjectFactory2 extends ITestObjectFactory { */ @Deprecated default Object newInstance(Class cls) { - return newInstance(cls, new Object[0]); + return newInstance((Class) cls, new Object[0]); } } diff --git a/testng-core-api/src/main/java/org/testng/ISuite.java b/testng-core-api/src/main/java/org/testng/ISuite.java index 773f95b541..0401597c73 100644 --- a/testng-core-api/src/main/java/org/testng/ISuite.java +++ b/testng-core-api/src/main/java/org/testng/ISuite.java @@ -4,6 +4,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.internal.annotations.IAnnotationFinder; import org.testng.xml.XmlSuite; @@ -14,25 +15,29 @@ */ public interface ISuite extends IAttributes { - /** @return the name of this suite. */ + /** Returns the name of this suite. */ String getName(); - /** @return The results for this suite. */ + /** Returns the results for this suite. */ Map getResults(); - /** @return The object factory used to create all test instances. */ + /** Returns the object factory used to create all test instances. */ ITestObjectFactory getObjectFactory(); + /** + * Returns null. + * + * @deprecated This interface stands deprecated as of TestNG 7.5.0 + */ @Deprecated - /** @deprecated - This interface stands deprecated as of TestNG 7.5.0 */ - default IObjectFactory2 getObjectFactory2() { + default @Nullable IObjectFactory2 getObjectFactory2() { return null; } - /** @return The output directory used for the reports. */ + /** Returns the output directory used for the reports. */ String getOutputDirectory(); - /** @return true if the tests must be run in parallel. */ + /** Returns true if the tests must be run in parallel. */ String getParallel(); String getParentModule(); @@ -40,8 +45,9 @@ default IObjectFactory2 getObjectFactory2() { String getGuiceStage(); /** + * Returns the value of this parameter, or null if none was specified. + * * @param parameterName The name of the parameter - * @return The value of this parameter, or null if none was specified. */ String getParameter(String parameterName); @@ -52,18 +58,18 @@ default IObjectFactory2 getObjectFactory2() { */ Map> getMethodsByGroups(); - /** @return a list of all the methods that were invoked in this suite. */ + /** Returns a list of all the methods that were invoked in this suite. */ List getAllInvokedMethods(); - /** @return All the methods that were not included in this test run. */ + /** Returns all the methods that were not included in this test run. */ Collection getExcludedMethods(); /** Triggers the start of running tests included in the suite. */ void run(); /** - * @return The host where this suite was run, or null if it was run locally. The returned string - * has the form: host:port + * Returns the host where this suite was run, or null if it was run locally. The returned string + * has the form: host:port */ String getHost(); @@ -74,10 +80,10 @@ default IObjectFactory2 getObjectFactory2() { */ SuiteRunState getSuiteState(); - /** @return the annotation finder used for the specified type (JDK5 or javadoc) */ + /** Returns the annotation finder used for the specified type (JDK5 or javadoc) */ IAnnotationFinder getAnnotationFinder(); - /** @return The representation of the current XML suite file. */ + /** Returns the representation of the current XML suite file. */ XmlSuite getXmlSuite(); void addListener(ITestNGListener listener); @@ -87,8 +93,8 @@ default IObjectFactory2 getObjectFactory2() { void setParentInjector(Injector injector); /** - * @return the total number of methods found in this suite. The presence of factories or data - * providers might cause the actual number of test methods run be bigger than this list. + * Returns the total number of methods found in this suite. The presence of factories or data + * providers might cause the actual number of test methods run be bigger than this list. */ List getAllMethods(); } diff --git a/testng-core-api/src/main/java/org/testng/ITestContext.java b/testng-core-api/src/main/java/org/testng/ITestContext.java index 5de1055d5b..c12d2d81da 100644 --- a/testng-core-api/src/main/java/org/testng/ITestContext.java +++ b/testng-core-api/src/main/java/org/testng/ITestContext.java @@ -2,6 +2,7 @@ import java.util.Collection; import java.util.Date; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.xml.XmlTest; /** @@ -13,29 +14,30 @@ */ public interface ITestContext extends IAttributes { - /** @return The name of this test. */ + /** Returns the name of this test. */ String getName(); - /** @return When this test started running. */ + /** Returns when this test started running. */ Date getStartDate(); - /** @return When this test stopped running. */ + /** Returns when this test stopped running. */ Date getEndDate(); - /** @return A list of all the tests that run successfully. */ + /** Returns a list of all the tests that run successfully. */ IResultMap getPassedTests(); - /** @return A list of all the tests that were skipped */ + /** Returns a list of all the tests that were skipped */ IResultMap getSkippedTests(); /** - * @return A list of all the tests that failed but are being ignored because annotated with a - * successPercentage. + * Returns a list of all the tests that failed but are being ignored because annotated with a + * successPercentage. */ IResultMap getFailedButWithinSuccessPercentageTests(); /** - * @return A map of all the tests that failed, indexed by their ITestNGMethod. + * Returns a map of all the tests that failed, indexed by their ITestNGMethod. + * * @see org.testng.ITestNGMethod */ IResultMap getFailedTests(); @@ -43,40 +45,40 @@ public interface ITestContext extends IAttributes { /** @return All the groups that are included for this test run. */ String[] getIncludedGroups(); - /** @return All the groups that are excluded for this test run. */ + /** Returns all the groups that are excluded for this test run. */ String[] getExcludedGroups(); - /** @return Where the reports will be generated. */ + /** Returns where the reports will be generated. */ String getOutputDirectory(); - /** @return The Suite object that was passed to the runner at start-up. */ + /** Returns the Suite object that was passed to the runner at start-up. */ ISuite getSuite(); - /** @return All the test methods that were run. */ + /** Returns all the test methods that were run. */ ITestNGMethod[] getAllTestMethods(); /** - * @return The host where this test was run, or null if it was run locally. The returned string - * has the form: host:port + * Returns the host where this test was run, or null if it was run locally. The returned string + * has the form: host:port */ String getHost(); - /** @return All the methods that were not included in this test run. */ + /** Returns all the methods that were not included in this test run. */ Collection getExcludedMethods(); - /** @return The information about the successful configuration method invocations. */ + /** Returns the information about the successful configuration method invocations. */ IResultMap getPassedConfigurations(); - /** @return The information about the skipped configuration method invocations. */ + /** Returns the information about the skipped configuration method invocations. */ IResultMap getSkippedConfigurations(); - /** @return The information about the failed configuration method invocations. */ + /** Returns the information about the failed configuration method invocations. */ IResultMap getFailedConfigurations(); - /** @return the current XmlTest. */ + /** Returns the current XmlTest. */ XmlTest getCurrentXmlTest(); - default IInjectorFactory getInjectorFactory() { + default @Nullable IInjectorFactory getInjectorFactory() { return null; } } diff --git a/testng-core-api/src/main/java/org/testng/ITestNGMethod.java b/testng-core-api/src/main/java/org/testng/ITestNGMethod.java index 0a5ec017f2..f890be6690 100644 --- a/testng-core-api/src/main/java/org/testng/ITestNGMethod.java +++ b/testng-core-api/src/main/java/org/testng/ITestNGMethod.java @@ -4,6 +4,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.annotations.CustomAttribute; import org.testng.internal.ConstructorOrMethod; import org.testng.internal.IParameterInfo; @@ -17,8 +18,8 @@ public interface ITestNGMethod extends Cloneable { /** - * @return The real class on which this method was declared (can be different from - * getMethod().getDeclaringClass() if the test method was defined in a superclass). + * Returns the real class on which this method was declared (can be different from + * getMethod().getDeclaringClass() if the test method was defined in a superclass). */ Class getRealClass(); @@ -48,16 +49,16 @@ public interface ITestNGMethod extends Cloneable { long[] getInstanceHashCodes(); /** - * @return The groups this method belongs to, possibly added to the groups declared on the class. + * Returns the groups this method belongs to, possibly added to the groups declared on the class. */ String[] getGroups(); /** - * @return The groups this method depends on, possibly added to the groups declared on the class. + * Returns the groups this method depends on, possibly added to the groups declared on the class. */ String[] getGroupsDependedUpon(); - /** @return If a group was not found. */ + /** Returns if a group was not found. */ String getMissingGroup(); void setMissingGroup(String group); @@ -67,24 +68,24 @@ public interface ITestNGMethod extends Cloneable { String[] getAfterGroups(); /** - * @return The methods this method depends on, possibly added to the methods declared on the - * class. + * Returns the methods this method depends on, possibly added to the methods declared on the + * class. */ String[] getMethodsDependedUpon(); /** - * @return - The set of methods that are dependent on the current method. This information can - * help in deciding what other TestNG methods will be skipped if the current method fails. If - * the current method is a configuration method, then an empty set is returned. + * Returns the set of methods that are dependent on the current method. This information can help + * in deciding what other TestNG methods will be skipped if the current method fails. If the + * current method is a configuration method, then an empty set is returned. */ default Set downstreamDependencies() { throw new UnsupportedOperationException("Pending implementation"); } /** - * @return - The set of methods upon which the current method has a dependency. This information - * can help in deciding what all TestNG methods need to pass before the current method can be - * executed. If the current method is a configuration method, then an empty set is returned. + * Returns the set of methods upon which the current method has a dependency. This information can + * help in deciding what all TestNG methods need to pass before the current method can be + * executed. If the current method is a configuration method, then an empty set is returned. */ default Set upstreamDependencies() { throw new UnsupportedOperationException("Pending implementation"); @@ -92,31 +93,31 @@ default Set upstreamDependencies() { void addMethodDependedUpon(String methodName); - /** @return true if this method was annotated with @Test */ + /** Returns true if this method was annotated with @Test */ boolean isTest(); - /** @return true if this method was annotated with @Configuration and beforeTestMethod = true */ + /** Returns true if this method was annotated with @Configuration and beforeTestMethod = true */ boolean isBeforeMethodConfiguration(); - /** @return true if this method was annotated with @Configuration and beforeTestMethod = false */ + /** Returns true if this method was annotated with @Configuration and beforeTestMethod = false */ boolean isAfterMethodConfiguration(); - /** @return true if this method was annotated with @Configuration and beforeClassMethod = true */ + /** Returns true if this method was annotated with @Configuration and beforeClassMethod = true */ boolean isBeforeClassConfiguration(); - /** @return true if this method was annotated with @Configuration and beforeClassMethod = false */ + /** Returns true if this method was annotated with @Configuration and beforeClassMethod = false */ boolean isAfterClassConfiguration(); - /** @return true if this method was annotated with @Configuration and beforeSuite = true */ + /** Returns true if this method was annotated with @Configuration and beforeSuite = true */ boolean isBeforeSuiteConfiguration(); - /** @return true if this method was annotated with @Configuration and afterSuite = true */ + /** Returns true if this method was annotated with @Configuration and afterSuite = true */ boolean isAfterSuiteConfiguration(); - /** @return true if this method is a @BeforeTest (@Configuration beforeTest=true) */ + /** Returns true if this method is a @BeforeTest (@Configuration beforeTest=true) */ boolean isBeforeTestConfiguration(); - /** @return true if this method is an @AfterTest (@Configuration afterTest=true) */ + /** Returns true if this method is an @AfterTest (@Configuration afterTest=true) */ boolean isAfterTestConfiguration(); boolean isBeforeGroupsConfiguration(); @@ -131,20 +132,20 @@ default boolean hasAfterGroupsConfiguration() { return false; } - /** @return The timeout in milliseconds. */ + /** Returns the timeout in milliseconds. */ long getTimeOut(); void setTimeOut(long timeOut); - /** @return the number of times this method needs to be invoked. */ + /** Returns the number of times this method needs to be invoked. */ int getInvocationCount(); void setInvocationCount(int count); - /** @return the success percentage for this method (between 0 and 100). */ + /** Returns the success percentage for this method (between 0 and 100). */ int getSuccessPercentage(); - /** @return The id of the thread this method was run in. */ + /** Returns the id of the thread this method was run in. */ String getId(); void setId(String id); @@ -154,15 +155,16 @@ default boolean hasAfterGroupsConfiguration() { void setDate(long date); /** + * Returns true if this ITestNGMethod can be invoked from within IClass. + * * @param testClass The test class - * @return true if this ITestNGMethod can be invoked from within IClass. */ boolean canRunFromClass(IClass testClass); - /** @return true if this method is alwaysRun=true */ + /** Returns true if this method is alwaysRun=true */ boolean isAlwaysRun(); - /** @return the number of threads to be used when invoking the method on parallel */ + /** Returns the number of threads to be used when invoking the method on parallel */ int getThreadPoolSize(); void setThreadPoolSize(int threadPoolSize); @@ -197,7 +199,7 @@ default boolean hasAfterGroupsConfiguration() { void setSkipFailedInvocations(boolean skip); - /** @return The time under which all invocationCount methods need to complete by. */ + /** Returns the time under which all invocationCount methods need to complete by. */ long getInvocationTimeOut(); boolean ignoreMissingDependencies(); @@ -239,21 +241,20 @@ default boolean hasAfterGroupsConfiguration() { void setInterceptedPriority(int priority); - /** @return the XmlTest this method belongs to. */ + /** Returns the XmlTest this method belongs to. */ XmlTest getXmlTest(); ConstructorOrMethod getConstructorOrMethod(); /** + * Returns the parameters found in the include tag, if any + * * @param test - The {@link XmlTest} object. - * @return the parameters found in the include tag, if any */ Map findMethodParameters(XmlTest test); /** - * getRealClass().getName() + "." + getMethodName() - * - * @return qualified name for this method + * Returns the qualified name for this method: getRealClass().getName() + "." + getMethodName() */ String getQualifiedName(); @@ -262,26 +263,26 @@ default boolean isDataDriven() { } /** - * @return - A {@link IParameterInfo} object that represents details about the parameters - * associated with the factory method. + * Returns a {@link IParameterInfo} object that represents details about the parameters associated + * with the factory method. */ - default IParameterInfo getFactoryMethodParamsInfo() { + default @Nullable IParameterInfo getFactoryMethodParamsInfo() { return null; } /** - * @return - An array of {@link CustomAttribute} that represents the custom attributes associated - * with a test. + * Returns an array of {@link CustomAttribute} that represents the custom attributes associated + * with a test. */ default CustomAttribute[] getAttributes() { return new CustomAttribute[] {}; } /** - * @return - An {@link IDataProviderMethod} for a data provider powered test method and null + * Returns an {@link IDataProviderMethod} for a data provider powered test method and null * otherwise. */ - default IDataProviderMethod getDataProviderMethod() { + default @Nullable IDataProviderMethod getDataProviderMethod() { return null; } @@ -290,8 +291,8 @@ default Class[] getParameterTypes() { } /** - * @return - true if the configuration failure arising out of this method should be - * ignored. + * Returns true if the configuration failure arising out of this method should be + * ignored. */ default boolean isIgnoreFailure() { return false; diff --git a/testng-core-api/src/main/java/org/testng/ITestResult.java b/testng-core-api/src/main/java/org/testng/ITestResult.java index 00649a4615..1d8812fd62 100644 --- a/testng-core-api/src/main/java/org/testng/ITestResult.java +++ b/testng-core-api/src/main/java/org/testng/ITestResult.java @@ -3,6 +3,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.internal.thread.ThreadTimeoutException; /** @@ -22,76 +23,76 @@ public interface ITestResult extends IAttributes, Comparable { int SUCCESS_PERCENTAGE_FAILURE = 4; int STARTED = 16; - /** @return The status of this result, using one of the constants above. */ + /** Returns the status of this result, using one of the constants above. */ int getStatus(); void setStatus(int status); - /** @return The test method this result represents. */ + /** Returns the test method this result represents. */ ITestNGMethod getMethod(); - /** @return The parameters this method was invoked with. */ + /** Returns the parameters this method was invoked with. */ Object[] getParameters(); void setParameters(Object[] parameters); - /** @return The test class used this object is a result for. */ + /** Returns the test class used this object is a result for. */ IClass getTestClass(); /** - * @return The throwable that was thrown while running the method, or null if no exception was - * thrown. + * Returns the throwable that was thrown while running the method, or null if no exception was + * thrown. */ Throwable getThrowable(); void setThrowable(Throwable throwable); - /** @return the start date for this test, in milliseconds. */ + /** Returns the start date for this test, in milliseconds. */ long getStartMillis(); - /** @return the end date for this test, in milliseconds. */ + /** Returns the end date for this test, in milliseconds. */ long getEndMillis(); void setEndMillis(long millis); - /** @return The name of this TestResult, typically identical to the name of the method. */ + /** Returns the name of this TestResult, typically identical to the name of the method. */ String getName(); - /** @return true if if this test run is a SUCCESS */ + /** Returns true if if this test run is a SUCCESS */ boolean isSuccess(); /** - * @return The host where this suite was run, or null if it was run locally. The returned string - * has the form: host:port + * Returns the host where this suite was run, or null if it was run locally. The returned string + * has the form: host:port */ String getHost(); - /** @return The instance on which this method was run. */ + /** Returns the instance on which this method was run. */ Object getInstance(); /** - * @return - A parameter array that was passed to a factory method (or) an empty object array - * otherwise. + * Returns a parameter array that was passed to a factory method (or) an empty object array + * otherwise. */ Object[] getFactoryParameters(); /** - * @return The test name if this result's related instance implements ITest or - * use @Test(testName=...), null otherwise. + * Returns the test name if this result's related instance implements ITest or + * use @Test(testName=...), null otherwise. */ String getTestName(); String getInstanceName(); - /** @return the {@link ITestContext} for this test result. */ + /** Returns the {@link ITestContext} for this test result. */ ITestContext getTestContext(); /** @param name - The new name to be used as a test name */ void setTestName(String name); /** - * @return - true if the test was retried again by an implementation of {@link - * IRetryAnalyzer} + * Returns true if the test was retried again by an implementation of {@link + * IRetryAnalyzer}. */ boolean wasRetried(); @@ -101,47 +102,49 @@ public interface ITestResult extends IAttributes, Comparable { void setWasRetried(boolean wasRetried); /** - * @return - The list of either upstream method(s) or configuration method(s) whose failure led to - * the current method being skipped. An empty list is returned when the current method is not - * a skipped method. + * Returns the list of either upstream method(s) or configuration method(s) whose failure led to + * the current method being skipped. An empty list is returned when the current method is not a + * skipped method. */ default List getSkipCausedBy() { return Collections.emptyList(); } /** - * @return - A unique id for the current JVM that represents a unique way of identifying a - * specific test method's result. + * Returns a unique id for the current JVM that represents a unique way of identifying a specific + * test method's result. */ String id(); /** - * @return - true if the current test result is either {@link ITestResult#STARTED} or - * {@link ITestResult#CREATED} + * Returns true if the current test result is either {@link ITestResult#STARTED} or + * {@link ITestResult#CREATED} */ default boolean isNotRunning() { return getStatus() == STARTED || getStatus() == CREATED; } /** - * @return - A list of all user facing statuses viz., - *
    - *
  • {@link ITestResult#SUCCESS} - *
  • {@link ITestResult#SUCCESS_PERCENTAGE_FAILURE} - *
  • {@link ITestResult#FAILURE} - *
  • {@link ITestResult#SKIP} - *
+ * Returns a list of all user facing statuses viz., + * + *
    + *
  • {@link ITestResult#SUCCESS} + *
  • {@link ITestResult#SUCCESS_PERCENTAGE_FAILURE} + *
  • {@link ITestResult#FAILURE} + *
  • {@link ITestResult#SKIP} + *
*/ static List finalStatuses() { return Arrays.asList("SUCCESS", "FAILURE", "SKIP", "SUCCESS_PERCENTAGE_FAILURE"); } /** + * Returns true if the test failure was due to a timeout. + * * @param result - The test result of a method - * @return - true if the test failure was due to a timeout. */ static boolean wasFailureDueToTimeout(ITestResult result) { - Throwable cause = result.getThrowable(); + @Nullable Throwable cause = result.getThrowable(); while (cause != null && !cause.getClass().equals(Throwable.class)) { if (cause instanceof ThreadTimeoutException) { return true; diff --git a/testng-core-api/src/main/java/org/testng/Reporter.java b/testng-core-api/src/main/java/org/testng/Reporter.java index e27356b6db..6d84975f62 100644 --- a/testng-core-api/src/main/java/org/testng/Reporter.java +++ b/testng-core-api/src/main/java/org/testng/Reporter.java @@ -3,6 +3,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.collections.Lists; import org.testng.collections.Maps; import org.testng.internal.Utils; @@ -27,7 +28,7 @@ public class Reporter { // When tests are run in parallel, each thread may be working with different // 'current test result'. Also, this value should be inherited if the test code // spawns its own thread. - private static final ThreadLocal m_currentTestResult = + private static final ThreadLocal<@Nullable ITestResult> m_currentTestResult = new InheritableThreadLocal<>(); /** All output logged in a sequential order. */ @@ -153,7 +154,7 @@ public static void log(String s, int level) { } /** @return the current test result. */ - public static ITestResult getCurrentTestResult() { + public static @Nullable ITestResult getCurrentTestResult() { return m_currentTestResult.get(); } diff --git a/testng-core-api/src/main/java/org/testng/TestNGException.java b/testng-core-api/src/main/java/org/testng/TestNGException.java index fd1e31c9bd..38f2d15356 100644 --- a/testng-core-api/src/main/java/org/testng/TestNGException.java +++ b/testng-core-api/src/main/java/org/testng/TestNGException.java @@ -1,5 +1,7 @@ package org.testng; +import org.checkerframework.checker.nullness.qual.Nullable; + /** The base class for all exceptions thrown by TestNG. */ public class TestNGException extends RuntimeException { @@ -13,7 +15,7 @@ public TestNGException(String string) { super("\n" + string); } - public TestNGException(String string, Throwable t) { + public TestNGException(String string, @Nullable Throwable t) { super("\n" + string, t); } } diff --git a/testng-core-api/src/main/java/org/testng/internal/ClassHelper.java b/testng-core-api/src/main/java/org/testng/internal/ClassHelper.java index 691992e62a..110c05e527 100644 --- a/testng-core-api/src/main/java/org/testng/internal/ClassHelper.java +++ b/testng-core-api/src/main/java/org/testng/internal/ClassHelper.java @@ -12,6 +12,7 @@ import java.util.Vector; import java.util.function.BiConsumer; import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.TestNGException; import org.testng.annotations.IFactoryAnnotation; import org.testng.collections.Lists; @@ -52,7 +53,7 @@ public static void addClassLoader(final ClassLoader loader) { static List appendContextualClassLoaders(List currentLoaders) { List allClassLoaders = Lists.newArrayList(); - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + @Nullable ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); if (contextClassLoader != null) { allClassLoaders.add(contextClassLoader); } @@ -68,7 +69,7 @@ static List appendContextualClassLoaders(List currentL * @param className the class name to be loaded. * @return the class or null if the class is not found. */ - public static Class forName(final String className) { + public static @Nullable Class forName(final String className) { List allClassLoaders = appendContextualClassLoaders(classLoaders); for (ClassLoader classLoader : allClassLoaders) { @@ -171,21 +172,19 @@ public static Set getAvailableMethods(Class clazz) { appendMethod(methods, declaredMethod); } - Class parent = clazz.getSuperclass(); - if (null != parent) { - while (!Object.class.equals(parent)) { - Set>> extractedMethods = - extractMethods(clazz, parent, methods).entrySet(); - for (Map.Entry> extractedMethod : extractedMethods) { - Set m = methods.get(extractedMethod.getKey()); - if (m == null) { - methods.put(extractedMethod.getKey(), extractedMethod.getValue()); - } else { - m.addAll(extractedMethod.getValue()); - } + @Nullable Class parent = clazz.getSuperclass(); + while (parent != null && !Object.class.equals(parent)) { + Set>> extractedMethods = + extractMethods(clazz, parent, methods).entrySet(); + for (Map.Entry> extractedMethod : extractedMethods) { + Set m = methods.get(extractedMethod.getKey()); + if (m == null) { + methods.put(extractedMethod.getKey(), extractedMethod.getValue()); + } else { + m.addAll(extractedMethod.getValue()); } - parent = parent.getSuperclass(); } + parent = parent.getSuperclass(); } Set returnValue = Sets.newHashSet(); @@ -231,7 +230,8 @@ private static boolean canInclude( return visible && hasNoInheritanceTraits; } - private static boolean isSamePackage(Package childPackage, Package classPackage) { + private static boolean isSamePackage( + @Nullable Package childPackage, @Nullable Package classPackage) { boolean isSamePackage = false; if ((null == childPackage) && (null == classPackage)) { diff --git a/testng-core-api/src/main/java/org/testng/internal/ConstructorOrMethod.java b/testng-core-api/src/main/java/org/testng/internal/ConstructorOrMethod.java index fcdd85b3db..1743d9b4dd 100644 --- a/testng-core-api/src/main/java/org/testng/internal/ConstructorOrMethod.java +++ b/testng-core-api/src/main/java/org/testng/internal/ConstructorOrMethod.java @@ -4,20 +4,23 @@ import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.util.Objects; +import org.checkerframework.checker.nullness.qual.Nullable; /** Encapsulation of either a method or a constructor. */ public class ConstructorOrMethod { - private Method m_method; - private Constructor m_constructor; + private @Nullable Method m_method; + private @Nullable Constructor m_constructor; private boolean m_enabled = true; public ConstructorOrMethod(Method m) { m_method = m; + m_constructor = null; } public ConstructorOrMethod(Constructor c) { m_constructor = c; + m_method = null; } public ConstructorOrMethod(Executable e) { @@ -44,11 +47,11 @@ public Class[] getParameterTypes() { : getConstructor().getParameterTypes(); } - public Method getMethod() { + public @Nullable Method getMethod() { return m_method; } - public Constructor getConstructor() { + public @Nullable Constructor getConstructor() { return m_constructor; } @@ -60,7 +63,7 @@ private Executable getInternalConstructorOrMethod() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/testng-core-api/src/main/java/org/testng/internal/PackageUtils.java b/testng-core-api/src/main/java/org/testng/internal/PackageUtils.java index 6669fb6ab4..347d4001f2 100644 --- a/testng-core-api/src/main/java/org/testng/internal/PackageUtils.java +++ b/testng-core-api/src/main/java/org/testng/internal/PackageUtils.java @@ -16,6 +16,7 @@ import java.util.function.Function; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.collections.Lists; import org.testng.internal.protocols.Input; import org.testng.internal.protocols.Processor; @@ -29,7 +30,7 @@ * @author Cedric Beust */ public class PackageUtils { - private static String[] testClassPaths; + private static @Nullable String[] testClassPaths; /** The additional class loaders to find classes in. */ private static final Collection classLoaders = new ConcurrentLinkedDeque<>(); @@ -79,12 +80,12 @@ public static String[] findClassesInPackage( .toArray(String[]::new); } - private static String[] getTestClasspath() { + private static @Nullable String[] getTestClasspath() { if (null != testClassPaths) { return testClassPaths; } - String testClasspath = RuntimeBehavior.getTestClasspath(); + @Nullable String testClasspath = RuntimeBehavior.getTestClasspath(); if (null == testClasspath) { return null; } @@ -124,7 +125,7 @@ private static Function> asURLs(String packageDir) { } private static boolean matchTestClasspath(URL url, String lastFragment, boolean recursive) { - String[] classpathFragments = getTestClasspath(); + @Nullable String[] classpathFragments = getTestClasspath(); if (null == classpathFragments) { return true; } diff --git a/testng-core-api/src/main/java/org/testng/internal/PropertyUtils.java b/testng-core-api/src/main/java/org/testng/internal/PropertyUtils.java index 29b53b18be..3e483a74de 100644 --- a/testng-core-api/src/main/java/org/testng/internal/PropertyUtils.java +++ b/testng-core-api/src/main/java/org/testng/internal/PropertyUtils.java @@ -8,6 +8,7 @@ import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.TestNGException; import org.testng.log4testng.Logger; @@ -21,7 +22,7 @@ public class PropertyUtils { private static final Logger LOGGER = Logger.getLogger(PropertyUtils.class); @SuppressWarnings("unchecked") - public static T convertType(Class type, String value, String paramName) { + public static @Nullable T convertType(Class type, String value, String paramName) { try { if (value == null || NULL_VALUE.equalsIgnoreCase(value)) { if (type.isPrimitive()) { @@ -70,14 +71,14 @@ public static T convertType(Class type, String value, String paramName) { throw new TestNGException("Unsupported type parameter : " + type); } - public static void setProperty(Object instance, String name, String value) { + public static void setProperty(@Nullable Object instance, String name, String value) { if (instance == null) { LOGGER.warn( "Cannot set property " + name + " with value " + value + ". The target instance is null"); return; } - Class propClass = getPropertyType(instance.getClass(), name); + @Nullable Class propClass = getPropertyType(instance.getClass(), name); if (propClass == null) { LOGGER.warn( "Cannot set property " @@ -88,64 +89,71 @@ public static void setProperty(Object instance, String name, String value) { return; } - Object realValue = convertType(propClass, value, name); + @Nullable Object realValue = convertType(propClass, value, name); // TODO: Here the property desc is searched again setPropertyRealValue(instance, name, realValue); } - public static Class getPropertyType(Class instanceClass, String propertyName) { + public static @Nullable Class getPropertyType( + @Nullable Class instanceClass, String propertyName) { if (instanceClass == null) { LOGGER.warn( "Cannot retrieve property class for " + propertyName + ". Target instance class is null"); } - PropertyDescriptor propDesc = getPropertyDescriptor(instanceClass, propertyName); + @Nullable PropertyDescriptor propDesc = getPropertyDescriptor(instanceClass, propertyName); if (propDesc == null) { return null; } return propDesc.getPropertyType(); } - private static PropertyDescriptor getPropertyDescriptor( - Class targetClass, String propertyName) { - PropertyDescriptor result = null; + private static @Nullable PropertyDescriptor getPropertyDescriptor( + @Nullable Class targetClass, String propertyName) { if (targetClass == null) { LOGGER.warn("Cannot retrieve property " + propertyName + ". Class is null"); - } else { - try { - BeanInfo beanInfo = Introspector.getBeanInfo(targetClass); - PropertyDescriptor[] propDescriptors = beanInfo.getPropertyDescriptors(); - for (PropertyDescriptor propDesc : propDescriptors) { - if (propDesc.getName().equals(propertyName)) { - result = propDesc; - break; - } + return null; + } + @Nullable PropertyDescriptor result = null; + try { + BeanInfo beanInfo = Introspector.getBeanInfo(targetClass); + PropertyDescriptor[] propDescriptors = beanInfo.getPropertyDescriptors(); + for (PropertyDescriptor propDesc : propDescriptors) { + if (propDesc.getName().equals(propertyName)) { + result = propDesc; + break; } - } catch (IntrospectionException ie) { - LOGGER.warn("Cannot retrieve property " + propertyName + ". Cause is: " + ie); } + } catch (IntrospectionException ie) { + LOGGER.warn("Cannot retrieve property " + propertyName + ". Cause is: " + ie); } return result; } - public static void setPropertyRealValue(Object instance, String name, Object value) { + public static void setPropertyRealValue( + @Nullable Object instance, String name, @Nullable Object value) { if (instance == null) { LOGGER.warn( "Cannot set property " + name + " with value " + value + ". Target instance is null"); return; } - PropertyDescriptor propDesc = getPropertyDescriptor(instance.getClass(), name); + @Nullable PropertyDescriptor propDesc = getPropertyDescriptor(instance.getClass(), name); if (propDesc == null) { LOGGER.warn( "Cannot set property " + name + " with value " + value + ". Property does not exist"); return; } - Method method = propDesc.getWriteMethod(); - try { - method.invoke(instance, value); - } catch (IllegalAccessException | InvocationTargetException iae) { - LOGGER.warn("Cannot set property " + name + " with value " + value + ". Cause " + iae); + @Nullable Method method = propDesc.getWriteMethod(); + if (method == null) { + LOGGER.warn( + "Cannot set property " + name + " with value " + value + ". Cause method not writeable"); + } else { + try { + method.invoke(instance, value); + } catch (IllegalAccessException | InvocationTargetException iae) { + LOGGER.warn("Cannot set property " + name + " with value " + value + ". Cause " + iae); + } } } } diff --git a/testng-core-api/src/main/java/org/testng/internal/ReporterConfig.java b/testng-core-api/src/main/java/org/testng/internal/ReporterConfig.java index 255550fb74..c51b16d205 100644 --- a/testng-core-api/src/main/java/org/testng/internal/ReporterConfig.java +++ b/testng-core-api/src/main/java/org/testng/internal/ReporterConfig.java @@ -1,6 +1,7 @@ package org.testng.internal; import java.util.List; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.collections.Lists; /** Stores the information regarding the configuration of a pluggable report listener. */ @@ -43,7 +44,7 @@ public String serialize() { return sb.toString(); } - public static ReporterConfig deserialize(String inputString) { + public static @Nullable ReporterConfig deserialize(String inputString) { if (Utils.isStringEmpty(inputString)) { return null; diff --git a/testng-core-api/src/main/java/org/testng/internal/RuntimeBehavior.java b/testng-core-api/src/main/java/org/testng/internal/RuntimeBehavior.java index 4879e57623..840466f5dd 100644 --- a/testng-core-api/src/main/java/org/testng/internal/RuntimeBehavior.java +++ b/testng-core-api/src/main/java/org/testng/internal/RuntimeBehavior.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Optional; import java.util.TimeZone; +import org.checkerframework.checker.nullness.qual.Nullable; /** This class houses handling all JVM arguments by TestNG */ public final class RuntimeBehavior { @@ -94,7 +95,7 @@ public static String orderMethodsBasedOn() { return System.getProperty("testng.order", Systematiser.Order.INSTANCES.getValue()); } - public static String getTestClasspath() { + public static @Nullable String getTestClasspath() { return System.getProperty(TEST_CLASSPATH); } diff --git a/testng-core-api/src/main/java/org/testng/internal/Systematiser.java b/testng-core-api/src/main/java/org/testng/internal/Systematiser.java index 183a562821..cff5a85ca6 100644 --- a/testng-core-api/src/main/java/org/testng/internal/Systematiser.java +++ b/testng-core-api/src/main/java/org/testng/internal/Systematiser.java @@ -3,6 +3,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.Objects; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.ITestNGMethod; /** Helps determine how should {@link ITestNGMethod} be ordered by TestNG. */ @@ -15,7 +16,7 @@ public final class Systematiser { .thenComparing(Object::toString) .thenComparing( method -> { - IParameterInfo paramsInfo = method.getFactoryMethodParamsInfo(); + @Nullable IParameterInfo paramsInfo = method.getFactoryMethodParamsInfo(); // TODO: avoid toString in parameter comparison return paramsInfo == null ? "" : Arrays.toString(paramsInfo.getParameters()); }) diff --git a/testng-core-api/src/main/java/org/testng/internal/Utils.java b/testng-core-api/src/main/java/org/testng/internal/Utils.java index 1ee0850721..d5e7e1ac2f 100644 --- a/testng-core-api/src/main/java/org/testng/internal/Utils.java +++ b/testng-core-api/src/main/java/org/testng/internal/Utils.java @@ -18,7 +18,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.ITestNGMethod; import org.testng.TestNGException; import org.testng.collections.Lists; @@ -131,7 +131,7 @@ public static void writeFile(@Nullable String outputDir, String fileName, String */ private static void writeFile( @Nullable File outputFolder, String fileNameParameter, String sb, @Nullable String encoding) { - File outDir = outputFolder; + @Nullable File outDir = outputFolder; String fileName = fileNameParameter; try { if (outDir == null) { @@ -224,7 +224,7 @@ public static void log(String msg) { * @param level the logging level of the message. * @param msg the message to log to System.out. */ - public static void log(String cls, int level, String msg) { + public static void log(String cls, int level, @Nullable String msg) { // Why this coupling on a static member of getVerbose()? if (getVerbose() >= level) { if (cls.length() > 0) { @@ -244,8 +244,8 @@ public static void warn(String warnMsg) { } /* Tokenize the string using the separator. */ - public static String[] split(String string, String sep) { - if ((string == null) || (string.length() == 0)) { + public static String[] split(@Nullable String string, String sep) { + if (string == null || string.isEmpty()) { return new String[0]; } @@ -270,12 +270,12 @@ public static String[] split(String string, String sep) { public static void writeResourceToFile(File file, String resourceName, Class clasz) throws IOException { - InputStream inputStream = clasz.getResourceAsStream("/" + resourceName); + @Nullable InputStream inputStream = clasz.getResourceAsStream("/" + resourceName); if (inputStream == null) { LOG.error("Couldn't find resource on the class path: " + resourceName); return; } - try { + try (inputStream) { try (FileOutputStream outputStream = new FileOutputStream(file)) { int nread; byte[] buffer = new byte[4096]; @@ -283,28 +283,26 @@ public static void writeResourceToFile(File file, String resourceName, Class outputStream.write(buffer, 0, nread); } } - } finally { - inputStream.close(); } } - public static String defaultIfStringEmpty(String s, String defaultValue) { + public static String defaultIfStringEmpty(@Nullable String s, String defaultValue) { return isStringEmpty(s) ? defaultValue : s; } - public static boolean isStringBlank(String s) { - return s == null || "".equals(s.trim()); + public static boolean isStringBlank(@Nullable String s) { + return s == null || s.trim().isEmpty(); } - public static boolean isStringEmpty(String s) { - return s == null || "".equals(s); + public static boolean isStringEmpty(@Nullable String s) { + return s == null || s.isEmpty(); } - public static boolean isStringNotBlank(String s) { + public static boolean isStringNotBlank(@Nullable String s) { return !isStringBlank(s); } - public static boolean isStringNotEmpty(String s) { + public static boolean isStringNotEmpty(@Nullable String s) { return !isStringEmpty(s); } @@ -360,7 +358,7 @@ private enum StackTraceType { FULL } - public static String escapeHtml(String s) { + public static @Nullable String escapeHtml(@Nullable String s) { if (s == null) { return null; } @@ -369,7 +367,7 @@ public static String escapeHtml(String s) { for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); - String nc = ESCAPES.get(c); + @Nullable String nc = ESCAPES.get(c); if (nc != null) { result.append(nc); } else { @@ -381,10 +379,6 @@ public static String escapeHtml(String s) { } public static String escapeUnicode(String s) { - if (s == null) { - return null; - } - StringBuilder result = new StringBuilder(); for (int i = 0; i < s.length(); i++) { @@ -403,11 +397,11 @@ static String filterTrace(String trace) { try { // first line contains the thrown exception - String line = bufferedReader.readLine(); - if (line == null) { + @Nullable String firstLine = bufferedReader.readLine(); + if (firstLine == null) { return ""; } - buf.append(line).append(LINE_SEP); + buf.append(firstLine).append(LINE_SEP); // // the stack frames of the trace @@ -415,6 +409,7 @@ static String filterTrace(String trace) { String[] excludedStrings = new String[] {"org.testng", "reflect", "org.gradle", "org.apache.maven.surefire"}; + String line; int excludedCount = 0; while ((line = bufferedReader.readLine()) != null) { boolean isExcluded = false; diff --git a/testng-core-api/src/main/java/org/testng/internal/objects/InstanceCreator.java b/testng-core-api/src/main/java/org/testng/internal/objects/InstanceCreator.java index a293981af8..6e5e244429 100644 --- a/testng-core-api/src/main/java/org/testng/internal/objects/InstanceCreator.java +++ b/testng-core-api/src/main/java/org/testng/internal/objects/InstanceCreator.java @@ -2,6 +2,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.TestNGException; import org.testng.internal.ClassHelper; @@ -15,7 +16,10 @@ private InstanceCreator() { } public static T newInstance(String className, Object... parameters) { - Class clazz = ClassHelper.forName(className); + @Nullable Class clazz = ClassHelper.forName(className); + if (clazz == null) { + throw new TestNGException(className + " was not found."); + } return (T) newInstance(clazz, parameters); } @@ -43,7 +47,7 @@ public static T newInstance(Constructor constructor, Object... parameters } public static T newInstance(Class cls, Object... parameters) { - Constructor ctor = null; + @Nullable Constructor ctor = null; for (Constructor c : cls.getConstructors()) { // Just comparing parameter array sizes. Comparing the parameter types // is more error prone since we need to take conversions into account diff --git a/testng-core-api/src/main/java/org/testng/internal/protocols/BundledResourceProcessor.java b/testng-core-api/src/main/java/org/testng/internal/protocols/BundledResourceProcessor.java index 79d0626b8c..bb55d65cfb 100644 --- a/testng-core-api/src/main/java/org/testng/internal/protocols/BundledResourceProcessor.java +++ b/testng-core-api/src/main/java/org/testng/internal/protocols/BundledResourceProcessor.java @@ -7,6 +7,7 @@ import java.net.URLConnection; import java.net.URLDecoder; import java.util.List; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.collections.Lists; class BundledResourceProcessor extends Processor { @@ -32,7 +33,10 @@ private static List processBundledResources( URLConnection connection = url.openConnection(); Method thisMethod = url.openConnection().getClass().getDeclaredMethod("getFileURL", params); Object[] paramsObj = {}; - URL fileUrl = (URL) thisMethod.invoke(connection, paramsObj); + @Nullable URL fileUrl = (URL) thisMethod.invoke(connection, paramsObj); + if (fileUrl == null) { + return Lists.newArrayList(); + } return findClassesInDirPackage( packageOnly, included, excluded, URLDecoder.decode(fileUrl.getFile(), UTF_8), recursive); } catch (Exception ex) { diff --git a/testng-core-api/src/main/java/org/testng/internal/protocols/Input.java b/testng-core-api/src/main/java/org/testng/internal/protocols/Input.java index 18fd4c23b0..1eb8904e23 100644 --- a/testng-core-api/src/main/java/org/testng/internal/protocols/Input.java +++ b/testng-core-api/src/main/java/org/testng/internal/protocols/Input.java @@ -53,7 +53,14 @@ public static final class Builder { private String packageDirName; private String packageName; - private Builder() {} + private Builder() { + included = Collections.emptyList(); + excluded = Collections.emptyList(); + packageWithoutWildCards = ""; + recursive = false; + packageDirName = ""; + packageName = ""; + } public static Builder newBuilder() { return new Builder(); diff --git a/testng-core-api/src/main/java/org/testng/log4testng/Logger.java b/testng-core-api/src/main/java/org/testng/log4testng/Logger.java index 7dad29acaa..adb21922d4 100644 --- a/testng-core-api/src/main/java/org/testng/log4testng/Logger.java +++ b/testng-core-api/src/main/java/org/testng/log4testng/Logger.java @@ -1,6 +1,7 @@ package org.testng.log4testng; import java.util.Map; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.LoggerFactory; import org.testng.collections.Maps; @@ -48,7 +49,7 @@ public boolean isTraceEnabled() { * * @param message the message object to log. */ - public void trace(Object message) { + public void trace(@Nullable Object message) { logger.trace("{}", message); } @@ -59,7 +60,7 @@ public void trace(Object message) { * @param message the message object to log. * @param t the exception to log, including its stack trace. */ - public void trace(Object message, Throwable t) { + public void trace(@Nullable Object message, Throwable t) { logger.trace("{}", message, t); } @@ -78,7 +79,7 @@ public boolean isDebugEnabled() { * * @param message the message object to log. */ - public void debug(Object message) { + public void debug(@Nullable Object message) { logger.debug("{}", message); } @@ -89,7 +90,7 @@ public void debug(Object message) { * @param message the message object to log. * @param t the exception to log, including its stack trace. */ - public void debug(Object message, Throwable t) { + public void debug(@Nullable Object message, Throwable t) { logger.debug("{}", message, t); } @@ -108,7 +109,7 @@ public boolean isInfoEnabled() { * * @param message the message object to log. */ - public void info(Object message) { + public void info(@Nullable Object message) { logger.info("{}", message); } @@ -119,7 +120,7 @@ public void info(Object message) { * @param message the message object to log. * @param t the exception to log, including its stack trace. */ - public void info(Object message, Throwable t) { + public void info(@Nullable Object message, Throwable t) { logger.info("{}", message, t); } @@ -129,7 +130,7 @@ public void info(Object message, Throwable t) { * * @param message the message object to log. */ - public void warn(Object message) { + public void warn(@Nullable Object message) { logger.warn("{}", message); } @@ -140,7 +141,7 @@ public void warn(Object message) { * @param message the message object to log. * @param t the exception to log, including its stack trace. */ - public void warn(Object message, Throwable t) { + public void warn(@Nullable Object message, Throwable t) { logger.warn("{}", message, t); } @@ -150,7 +151,7 @@ public void warn(Object message, Throwable t) { * * @param message the message object to log. */ - public void error(Object message) { + public void error(@Nullable Object message) { logger.error("{}", message); } @@ -161,7 +162,7 @@ public void error(Object message) { * @param message the message object to log. * @param t the exception to log, including its stack trace. */ - public void error(Object message, Throwable t) { + public void error(@Nullable Object message, Throwable t) { logger.error("{}", message, t); } @@ -171,7 +172,7 @@ public void error(Object message, Throwable t) { * * @param message the message object to log. */ - public void fatal(Object message) { + public void fatal(@Nullable Object message) { logger.error("{}", message); } @@ -182,7 +183,7 @@ public void fatal(Object message) { * @param message the message object to log. * @param t the exception to log, including its stack trace. */ - public void fatal(Object message, Throwable t) { + public void fatal(@Nullable Object message, Throwable t) { logger.error("{}", message, t); } diff --git a/testng-core-api/src/main/java/org/testng/package-info.java b/testng-core-api/src/main/java/org/testng/package-info.java new file mode 100644 index 0000000000..bf86fddb16 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/package-info.java @@ -0,0 +1,6 @@ +@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.ALL) +package org.testng; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.checkerframework.framework.qual.TypeUseLocation; diff --git a/testng-core-api/src/main/java/org/testng/reporters/XMLStringBuffer.java b/testng-core-api/src/main/java/org/testng/reporters/XMLStringBuffer.java index 259637579c..5f0b7f6b9c 100644 --- a/testng-core-api/src/main/java/org/testng/reporters/XMLStringBuffer.java +++ b/testng-core-api/src/main/java/org/testng/reporters/XMLStringBuffer.java @@ -28,7 +28,7 @@ public class XMLStringBuffer { /** A string of space character representing the current indentation. */ private String m_currentIndent = ""; - private String defaultComment = null; + private @Nullable String defaultComment = null; public XMLStringBuffer() { init(Buffer.create(), "", "1.0", "UTF-8"); @@ -61,7 +61,7 @@ private void init( IBuffer buffer, String start, @Nullable String version, @Nullable String encoding) { m_buffer = buffer; m_currentIndent = start; - if (version != null) { + if (version != null && encoding != null) { setXmlDetails(version, encoding); } } @@ -160,7 +160,7 @@ public void pop() { * * @param tagName The name of the tag this pop() is supposed to match. */ - public void pop(String tagName) { + public void pop(@Nullable String tagName) { m_currentIndent = m_currentIndent.substring(DEFAULT_INDENT_INCREMENT.length()); Tag t = m_tagStack.pop(); if (null != tagName) { @@ -171,7 +171,7 @@ public void pop(String tagName) { } } - String comment = defaultComment; + @Nullable String comment = defaultComment; if (comment == null) { comment = XMLUtils.extractComment(tagName, t.properties); } @@ -293,11 +293,11 @@ public void addString(String s) { m_buffer.append(s); } - public void setDefaultComment(String defaultComment) { + public void setDefaultComment(@Nullable String defaultComment) { this.defaultComment = defaultComment; } - public void addCDATA(String content) { + public void addCDATA(@Nullable String content) { if (content != null) { // Solution from https://coderanch.com/t/455930/java/Remove-control-characters content = content.replaceAll("[\\p{Cc}&&[^\\r\\n]]", ""); @@ -352,9 +352,9 @@ public void toWriter(Writer fw) { class Tag { public final String tagName; public final String indent; - public final Properties properties; + public final @Nullable Properties properties; - public Tag(String ind, String n, Properties p) { + public Tag(String ind, String n, @Nullable Properties p) { tagName = n; indent = ind; properties = p; diff --git a/testng-core-api/src/main/java/org/testng/reporters/XMLUtils.java b/testng-core-api/src/main/java/org/testng/reporters/XMLUtils.java index 6f29acdc64..8dbefd0d05 100644 --- a/testng-core-api/src/main/java/org/testng/reporters/XMLUtils.java +++ b/testng-core-api/src/main/java/org/testng/reporters/XMLUtils.java @@ -42,7 +42,8 @@ public static String xml( return result.toString(); } - public static String extractComment(String tag, Properties properties) { + public static @Nullable String extractComment( + @Nullable String tag, @Nullable Properties properties) { if (properties == null || "span".equals(tag)) return null; String[] attributes = new String[] {"id", "name", "class"}; @@ -68,7 +69,7 @@ public static void xmlOptional( String sp, String elementName, @Nullable String value, - Properties attributes) { + @Nullable Properties attributes) { if (null != value) { xmlRequired(result, sp, elementName, value, attributes); } @@ -83,7 +84,8 @@ public static void xmlRequired( result.append(xml(sp, elementName, value, attributes)); } - public static void xmlOpen(IBuffer result, String indent, String tag, Properties attributes) { + public static void xmlOpen( + IBuffer result, String indent, String tag, @Nullable Properties attributes) { xmlOpen(result, indent, tag, attributes, false /* no newline */); } @@ -94,7 +96,7 @@ public static void xmlOpen(IBuffer result, String indent, String tag, Properties * @param result the buffer to append attributes to. * @param attributes the attributes to append (may be null). */ - public static void appendAttributes(IBuffer result, Properties attributes) { + public static void appendAttributes(IBuffer result, @Nullable Properties attributes) { if (null != attributes) { for (Object element : attributes.entrySet()) { Entry entry = (Entry) element; @@ -106,7 +108,11 @@ public static void appendAttributes(IBuffer result, Properties attributes) { } public static void xmlOpen( - IBuffer result, String indent, String tag, Properties attributes, boolean noNewLine) { + IBuffer result, + String indent, + String tag, + @Nullable Properties attributes, + boolean noNewLine) { result.append(indent).append("<").append(tag); appendAttributes(result, attributes); result.append(">"); @@ -115,7 +121,7 @@ public static void xmlOpen( } } - public static void xmlClose(IBuffer result, String indent, String tag, String comment) { + public static void xmlClose(IBuffer result, String indent, String tag, @Nullable String comment) { result .append(indent) .append(" m_includedMethods = Lists.newArrayList(); private List m_excludedMethods = Lists.newArrayList(); - private String m_name = null; - private Class m_class = null; + private String m_name; + private @Nullable Class m_class = null; /** The index of this class in the <test> tag */ private int m_index; /** True if the classes need to be loaded */ @@ -26,58 +27,60 @@ public class XmlClass implements Cloneable { private XmlTest m_xmlTest; public XmlClass() { - init("", null, 0, false /* load classes */); + this("", null, 0, false /* load classes */); } public XmlClass(String name) { - init(name, null, 0); + this(name, null, 0); } public XmlClass(String name, boolean loadClasses) { - init(name, null, 0, loadClasses); + this(name, null, 0, loadClasses); } - public XmlClass(Class cls) { - init(cls.getName(), cls, 0, true); + public XmlClass(Class cls) { + this(cls.getName(), cls, 0, true); } - public XmlClass(Class cls, boolean loadClasses) { - init(cls.getName(), cls, 0, loadClasses); + public XmlClass(Class cls, boolean loadClasses) { + this(cls.getName(), cls, 0, loadClasses); } public XmlClass(String className, int index) { - init(className, null, index, true /* load classes */); + this(className, null, index, true /* load classes */); } public XmlClass(String className, int index, boolean loadClasses) { - init(className, null, index, loadClasses); + this(className, null, index, loadClasses); } - private void init(String className, Class cls, int index) { - init(className, cls, index, true /* load classes */); + private XmlClass(String className, @Nullable Class cls, int index) { + this(className, cls, index, true /* load classes */); } - private void init(String className, Class cls, int index, boolean resolveClass) { + private XmlClass(String className, @Nullable Class cls, int index, boolean resolveClass) { m_name = className; - m_class = cls; - m_index = index; - - if (null == m_class && resolveClass) { - loadClass(); + if (cls == null && resolveClass) { + m_class = loadClass(m_name); + } else { + m_class = null; } + m_index = index; } - private void loadClass() { - m_class = ClassHelper.forName(m_name); - - if (null == m_class) { - throw new TestNGException("Cannot find class in classpath: " + m_name); + private static Class loadClass(String name) { + @Nullable Class clazz = ClassHelper.forName(name); + if (clazz == null) { + throw new TestNGException("Cannot find class in classpath: " + name); } + return clazz; } /** @return Returns the className. */ public Class getSupportClass() { - if (m_class == null) loadClass(); + if (m_class == null) { + m_class = loadClass(m_name); + } return m_class; } @@ -210,19 +213,19 @@ public int hashCode() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } - if (obj == null) return XmlSuite.f(); - if (getClass() != obj.getClass()) return XmlSuite.f(); + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; XmlClass other = (XmlClass) obj; if (other.m_loadClasses != m_loadClasses) { - return XmlSuite.f(); + return false; } if (m_name == null) { - if (other.m_name != null) return XmlSuite.f(); - } else if (!m_name.equals(other.m_name)) return XmlSuite.f(); + if (other.m_name != null) return false; + } else if (!m_name.equals(other.m_name)) return false; return true; } diff --git a/testng-core-api/src/main/java/org/testng/xml/XmlDefine.java b/testng-core-api/src/main/java/org/testng/xml/XmlDefine.java index ea74ff06ef..8dd236a58a 100644 --- a/testng-core-api/src/main/java/org/testng/xml/XmlDefine.java +++ b/testng-core-api/src/main/java/org/testng/xml/XmlDefine.java @@ -3,6 +3,7 @@ import static org.testng.collections.CollectionUtils.hasElements; import java.util.List; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.collections.Lists; import org.testng.reporters.XMLStringBuffer; @@ -45,7 +46,7 @@ public List getIncludes() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/testng-core-api/src/main/java/org/testng/xml/XmlInclude.java b/testng-core-api/src/main/java/org/testng/xml/XmlInclude.java index 289bdfc31e..bad68c4fc2 100644 --- a/testng-core-api/src/main/java/org/testng/xml/XmlInclude.java +++ b/testng-core-api/src/main/java/org/testng/xml/XmlInclude.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.collections.Lists; import org.testng.collections.Maps; import org.testng.reporters.XMLStringBuffer; @@ -103,21 +104,21 @@ public int hashCode() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; - if (obj == null) return XmlSuite.f(); - if (getClass() != obj.getClass()) return XmlSuite.f(); + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; XmlInclude other = (XmlInclude) obj; // if (m_index != other.m_index) - // return XmlSuite.f(); + // return false; if (m_invocationNumbers == null) { - if (other.m_invocationNumbers != null) return XmlSuite.f(); - } else if (!m_invocationNumbers.equals(other.m_invocationNumbers)) return XmlSuite.f(); + if (other.m_invocationNumbers != null) return false; + } else if (!m_invocationNumbers.equals(other.m_invocationNumbers)) return false; if (m_name == null) { - if (other.m_name != null) return XmlSuite.f(); - } else if (!m_name.equals(other.m_name)) return XmlSuite.f(); + if (other.m_name != null) return false; + } else if (!m_name.equals(other.m_name)) return false; if (!m_parameters.equals(other.m_parameters)) { - return XmlSuite.f(); + return false; } return true; } diff --git a/testng-core-api/src/main/java/org/testng/xml/XmlMethodSelector.java b/testng-core-api/src/main/java/org/testng/xml/XmlMethodSelector.java index be7ac5feb6..010616a134 100644 --- a/testng-core-api/src/main/java/org/testng/xml/XmlMethodSelector.java +++ b/testng-core-api/src/main/java/org/testng/xml/XmlMethodSelector.java @@ -1,42 +1,43 @@ package org.testng.xml; import java.util.Properties; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.TestNGException; import org.testng.reporters.XMLStringBuffer; /** This class describes the tag <method-selector> in testng.xml. */ public class XmlMethodSelector { // Either this: - private String m_className; + private @Nullable String m_className; private int m_priority; // Or that: - private XmlScript m_script; + private @Nullable XmlScript m_script; // For YAML - public void setClassName(String s) { + public void setClassName(@Nullable String s) { m_className = s; } - public String getClassName() { + public @Nullable String getClassName() { return m_className; } // For YAML - public void setElement(String name, String priority) { + public void setElement(@Nullable String name, String priority) { setName(name); setPriority(Integer.parseInt(priority)); } - public void setName(String name) { + public void setName(@Nullable String name) { m_className = name; } - public XmlScript getScript() { + public @Nullable XmlScript getScript() { return m_script; } - public void setScript(XmlScript script) { + public void setScript(@Nullable XmlScript script) { m_script = script; } @@ -55,19 +56,26 @@ public String toXml(String indent) { if (null != m_className) { Properties clsProp = new Properties(); - clsProp.setProperty("name", getClassName()); + clsProp.setProperty("name", m_className); if (getPriority() != -1) { clsProp.setProperty("priority", String.valueOf(getPriority())); } xsb.addEmptyElement("selector-class", clsProp); - } else if (getScript() != null && getScript().getLanguage() != null) { + } else { + @Nullable XmlScript script = getScript(); + if (script == null) { + throw new TestNGException("Invalid Method Selector: found neither class name nor script"); + } + @Nullable String language = script.getLanguage(); + @Nullable String expression = script.getExpression(); + if (language == null || expression == null) { + throw new TestNGException("Invalid Method Selector: found neither expression nor language"); + } Properties scriptProp = new Properties(); - scriptProp.setProperty("language", getScript().getLanguage()); + scriptProp.setProperty("language", language); xsb.push("script", scriptProp); - xsb.addCDATA(getScript().getExpression()); + xsb.addCDATA(expression); xsb.pop("script"); - } else { - throw new TestNGException("Invalid Method Selector: found neither class name nor language"); } xsb.pop("method-selector"); @@ -80,45 +88,43 @@ public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((m_className == null) ? 0 : m_className.hashCode()); - if (getScript() != null) { - result = - prime * result - + ((getScript().getExpression() == null) - ? 0 - : getScript().getExpression().hashCode()); - result = - prime * result - + ((getScript().getLanguage() == null) ? 0 : getScript().getLanguage().hashCode()); + @Nullable XmlScript script = getScript(); + if (script != null) { + @Nullable String expression = script.getExpression(); + @Nullable String language = script.getLanguage(); + result = prime * result + ((expression == null) ? 0 : expression.hashCode()); + result = prime * result + ((language == null) ? 0 : language.hashCode()); } result = prime * result + m_priority; return result; } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; - if (obj == null) return XmlSuite.f(); - if (getClass() != obj.getClass()) return XmlSuite.f(); + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; XmlMethodSelector other = (XmlMethodSelector) obj; if (m_className == null) { - if (other.m_className != null) return XmlSuite.f(); - } else if (!m_className.equals(other.m_className)) return XmlSuite.f(); - if (getScript() == null || getScript().getExpression() == null) { - if (other.getScript() != null && other.getScript().getExpression() != null) - return XmlSuite.f(); - } else if (!getScript() + if (other.m_className != null) return false; + } else if (!m_className.equals(other.m_className)) return false; + @Nullable XmlScript script = getScript(); + @Nullable XmlScript otherScript = other.getScript(); + if (script == null || script.getExpression() == null) { + if (otherScript != null && otherScript.getExpression() != null) return false; + } else if (!script .getExpression() - .equals(other.getScript() == null ? null : other.getScript().getExpression())) { - return XmlSuite.f(); + .equals(otherScript == null ? null : otherScript.getExpression())) { + return false; } - if (getScript() == null || getScript().getLanguage() == null) { - if (other.getScript() != null && other.getScript().getLanguage() != null) return XmlSuite.f(); - } else if (!getScript() + if (script == null || script.getLanguage() == null) { + if (otherScript != null && otherScript.getLanguage() != null) return false; + } else if (!script .getLanguage() - .equals(other.getScript() == null ? null : other.getScript().getLanguage())) { - return XmlSuite.f(); + .equals(otherScript == null ? null : otherScript.getLanguage())) { + return false; } - if (m_priority != other.m_priority) return XmlSuite.f(); + if (m_priority != other.m_priority) return false; return true; } } diff --git a/testng-core-api/src/main/java/org/testng/xml/XmlPackage.java b/testng-core-api/src/main/java/org/testng/xml/XmlPackage.java index db5a5c2e8b..ee436c1275 100644 --- a/testng-core-api/src/main/java/org/testng/xml/XmlPackage.java +++ b/testng-core-api/src/main/java/org/testng/xml/XmlPackage.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.util.List; import java.util.Properties; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.collections.Lists; import org.testng.internal.PackageUtils; import org.testng.internal.Utils; @@ -15,11 +16,12 @@ public class XmlPackage { private String m_name; private List m_include = Lists.newArrayList(); private List m_exclude = Lists.newArrayList(); - private List m_xmlClasses = null; + private @Nullable List m_xmlClasses = null; + @SuppressWarnings("initialization.fields.uninitialized") + // For YAML public XmlPackage() {} - // For YAML public XmlPackage(String name) { m_name = name; } @@ -117,23 +119,23 @@ public int hashCode() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; - if (obj == null) return XmlSuite.f(); - if (getClass() != obj.getClass()) return XmlSuite.f(); + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; XmlPackage other = (XmlPackage) obj; if (m_exclude == null) { - if (other.m_exclude != null) return XmlSuite.f(); - } else if (!m_exclude.equals(other.m_exclude)) return XmlSuite.f(); + if (other.m_exclude != null) return false; + } else if (!m_exclude.equals(other.m_exclude)) return false; if (m_include == null) { - if (other.m_include != null) return XmlSuite.f(); - } else if (!m_include.equals(other.m_include)) return XmlSuite.f(); + if (other.m_include != null) return false; + } else if (!m_include.equals(other.m_include)) return false; if (m_name == null) { - if (other.m_name != null) return XmlSuite.f(); - } else if (!m_name.equals(other.m_name)) return XmlSuite.f(); + if (other.m_name != null) return false; + } else if (!m_name.equals(other.m_name)) return false; if (m_xmlClasses == null) { - if (other.m_xmlClasses != null) return XmlSuite.f(); - } else if (!m_xmlClasses.equals(other.m_xmlClasses)) return XmlSuite.f(); + if (other.m_xmlClasses != null) return false; + } else if (!m_xmlClasses.equals(other.m_xmlClasses)) return false; return true; } } diff --git a/testng-core-api/src/main/java/org/testng/xml/XmlScript.java b/testng-core-api/src/main/java/org/testng/xml/XmlScript.java index 63d072f672..0f94f04fc1 100644 --- a/testng-core-api/src/main/java/org/testng/xml/XmlScript.java +++ b/testng-core-api/src/main/java/org/testng/xml/XmlScript.java @@ -1,9 +1,11 @@ package org.testng.xml; +import org.checkerframework.checker.nullness.qual.Nullable; + public class XmlScript { - private String language; - private String expression; + private @Nullable String language = null; + private @Nullable String expression = null; public void setLanguage(String language) { this.language = language; @@ -13,11 +15,11 @@ public void setExpression(String expression) { this.expression = expression; } - public String getExpression() { + public @Nullable String getExpression() { return expression; } - public String getLanguage() { + public @Nullable String getLanguage() { return language; } } diff --git a/testng-core-api/src/main/java/org/testng/xml/XmlSuite.java b/testng-core-api/src/main/java/org/testng/xml/XmlSuite.java index f8a11b936f..a4c8ada59c 100644 --- a/testng-core-api/src/main/java/org/testng/xml/XmlSuite.java +++ b/testng-core-api/src/main/java/org/testng/xml/XmlSuite.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.ITestObjectFactory; import org.testng.collections.Lists; import org.testng.collections.Maps; @@ -86,7 +86,7 @@ public enum FailurePolicy { this.name = name; } - public static FailurePolicy getValidPolicy(String policy) { + public static @Nullable FailurePolicy getValidPolicy(String policy) { if (policy == null) { return null; } @@ -103,8 +103,6 @@ public String toString() { } } - private String m_test; - /** The default suite name TODO CQ is this OK as a default name. */ private static final String DEFAULT_SUITE_NAME = "Default Suite"; @@ -114,7 +112,7 @@ public String toString() { /** The suite verbose flag (0 to 10). */ public static final Integer DEFAULT_VERBOSE = 1; - private Integer m_verbose = null; + private @Nullable Integer m_verbose = null; public static final ParallelMode DEFAULT_PARALLEL = ParallelMode.NONE; private ParallelMode m_parallel = DEFAULT_PARALLEL; @@ -169,16 +167,16 @@ public String toString() { private Map m_parameters = Maps.newHashMap(); /** Name of the XML file. */ - private String m_fileName; + private @Nullable String m_fileName; /** Time out for methods/tests. */ - private String m_timeOut; + private @Nullable String m_timeOut; /** List of child XML suites specified using tags. */ private final List m_childSuites = Lists.newArrayList(); /** Parent XML suite if this suite was specified in another suite using tag. */ - private XmlSuite m_parentSuite; + private @Nullable XmlSuite m_parentSuite; private List m_suiteFiles = Lists.newArrayList(); @@ -204,12 +202,12 @@ public boolean isParsed() { } /** @return The fileName. */ - public String getFileName() { + public @Nullable String getFileName() { return m_fileName; } /** @param fileName The fileName to set. */ - public void setFileName(String fileName) { + public void setFileName(@Nullable String fileName) { m_fileName = fileName; } @@ -328,12 +326,13 @@ public void setName(String name) { } /** - * Returns the test. - * + * @deprecated Returns the test. * @return The test. */ + @Deprecated public String getTest() { - return m_test; + throw new UnsupportedOperationException( + "getTest() is not supported anymore. Please use getTests() instead."); } /** @@ -386,11 +385,9 @@ private void updateParameters() { * suite override the same named parameters from the parent suite. */ if (m_parentSuite != null) { - Set keySet = m_parentSuite.getParameters().keySet(); - for (String name : keySet) { - if (!m_parameters.containsKey(name)) { - m_parameters.put(name, m_parentSuite.getParameter(name)); - } + Map parentParameters = m_parentSuite.getParameters(); + for (Map.Entry parentParameter : parentParameters.entrySet()) { + m_parameters.putIfAbsent(parentParameter.getKey(), parentParameter.getValue()); } } } @@ -432,7 +429,7 @@ public Map getAllParameters() { * @param name The parameter name. * @return The parameter defined in this suite only. */ - public String getParameter(String name) { + public @Nullable String getParameter(String name) { return m_parameters.get(name); } @@ -599,7 +596,7 @@ public XmlSuite shallowCopy() { * * @param timeOut The timeout. */ - public void setTimeOut(String timeOut) { + public void setTimeOut(@Nullable String timeOut) { m_timeOut = timeOut; } @@ -608,7 +605,7 @@ public void setTimeOut(String timeOut) { * * @return The timeout. */ - public String getTimeOut() { + public @Nullable String getTimeOut() { return m_timeOut; } @@ -671,12 +668,12 @@ public int getDataProviderThreadCount() { return m_dataProviderThreadCount; } - public void setParentSuite(XmlSuite parentSuite) { + public void setParentSuite(@Nullable XmlSuite parentSuite) { m_parentSuite = parentSuite; updateParameters(); } - public XmlSuite getParentSuite() { + public @Nullable XmlSuite getParentSuite() { return m_parentSuite; } @@ -710,7 +707,6 @@ public int hashCode() { ? 0 : m_skipFailedInvocationCounts.hashCode()); result = prime * result + ((m_suiteFiles == null) ? 0 : m_suiteFiles.hashCode()); - result = prime * result + ((m_test == null) ? 0 : m_test.hashCode()); result = prime * result + ((m_tests == null) ? 0 : m_tests.hashCode()); result = prime * result + m_threadCount; result = prime * result + ((m_timeOut == null) ? 0 : m_timeOut.hashCode()); @@ -719,114 +715,107 @@ public int hashCode() { return result; } - /** Used to debug equals() bugs. */ - static boolean f() { - return false; - } - @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } if (obj == null) { - return f(); + return false; } if (getClass() != obj.getClass()) { - return f(); + return false; } XmlSuite other = (XmlSuite) obj; // if (m_childSuites == null) { // if (other.m_childSuites != null) - // return f(); + // return false; // } else if (!m_childSuites.equals(other.m_childSuites)) - // return f(); + // return false; if (m_configFailurePolicy == null) { if (other.m_configFailurePolicy != null) { - return f(); + return false; } } else if (!m_configFailurePolicy.equals(other.m_configFailurePolicy)) { - return f(); + return false; } if (m_dataProviderThreadCount != other.m_dataProviderThreadCount) { - return f(); + return false; } if (m_isJUnit == null) { if (other.m_isJUnit != null) { - return f(); + return false; } } else if (!m_isJUnit.equals(other.m_isJUnit)) { - return f(); + return false; } if (m_listeners == null) { if (other.m_listeners != null) { - return f(); + return false; } } else if (!m_listeners.equals(other.m_listeners)) { - return f(); + return false; } if (m_methodSelectors == null) { if (other.m_methodSelectors != null) { - return f(); + return false; } } else if (!m_methodSelectors.equals(other.m_methodSelectors)) { - return f(); + return false; } if (m_name == null) { if (other.m_name != null) { - return f(); + return false; } } else if (!m_name.equals(other.m_name)) { - return f(); + return false; } if (m_objectFactoryClass == null) { if (other.m_objectFactoryClass != null) { - return f(); + return false; } } else if (!m_objectFactoryClass.equals(other.m_objectFactoryClass)) { - return f(); + return false; } if (m_parallel == null) { if (other.m_parallel != null) { - return f(); + return false; } } else if (!m_parallel.equals(other.m_parallel)) { - return f(); + return false; } // if (m_parameters == null) { // if (other.m_parameters != null) { - // return f(); + // return false; // } // } else if (!m_parameters.equals(other.m_parameters)) { - // return f(); + // return false; // } // if (m_parentSuite == null) { // if (other.m_parentSuite != null) - // return f(); + // return false; // } else if (!m_parentSuite.equals(other.m_parentSuite)) - // return f(); + // return false; if (m_skipFailedInvocationCounts == null) { - if (other.m_skipFailedInvocationCounts != null) return f(); - } else if (!m_skipFailedInvocationCounts.equals(other.m_skipFailedInvocationCounts)) return f(); + if (other.m_skipFailedInvocationCounts != null) return false; + } else if (!m_skipFailedInvocationCounts.equals(other.m_skipFailedInvocationCounts)) + return false; if (m_suiteFiles == null) { - if (other.m_suiteFiles != null) return f(); - } else if (!m_suiteFiles.equals(other.m_suiteFiles)) return f(); - if (m_test == null) { - if (other.m_test != null) return f(); - } else if (!m_test.equals(other.m_test)) return f(); + if (other.m_suiteFiles != null) return false; + } else if (!m_suiteFiles.equals(other.m_suiteFiles)) return false; if (m_tests == null) { - if (other.m_tests != null) return f(); - } else if (!m_tests.equals(other.m_tests)) return f(); - if (m_threadCount != other.m_threadCount) return f(); + if (other.m_tests != null) return false; + } else if (!m_tests.equals(other.m_tests)) return false; + if (m_threadCount != other.m_threadCount) return false; if (m_timeOut == null) { - if (other.m_timeOut != null) return f(); - } else if (!m_timeOut.equals(other.m_timeOut)) return f(); + if (other.m_timeOut != null) return false; + } else if (!m_timeOut.equals(other.m_timeOut)) return false; if (m_verbose == null) { - if (other.m_verbose != null) return f(); - } else if (!m_verbose.equals(other.m_verbose)) return f(); + if (other.m_verbose != null) return false; + } else if (!m_verbose.equals(other.m_verbose)) return false; if (m_xmlPackages == null) { - if (other.m_xmlPackages != null) return f(); - } else if (!m_xmlPackages.equals(other.m_xmlPackages)) return f(); + if (other.m_xmlPackages != null) return false; + } else if (!m_xmlPackages.equals(other.m_xmlPackages)) return false; return true; } diff --git a/testng-core-api/src/main/java/org/testng/xml/XmlTest.java b/testng-core-api/src/main/java/org/testng/xml/XmlTest.java index e408e0f327..c8f21d3e8d 100644 --- a/testng-core-api/src/main/java/org/testng/xml/XmlTest.java +++ b/testng-core-api/src/main/java/org/testng/xml/XmlTest.java @@ -1,6 +1,7 @@ package org.testng.xml; import java.util.*; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.TestNGException; import org.testng.collections.Lists; import org.testng.collections.Maps; @@ -19,7 +20,7 @@ public class XmlTest implements Cloneable { private List m_xmlClasses = Lists.newArrayList(); private Map m_parameters = Maps.newHashMap(); - private XmlSuite.ParallelMode m_parallel; + private XmlSuite.ParallelMode m_parallel = null; private List m_methodSelectors = Lists.newArrayList(); // test level packages @@ -27,15 +28,15 @@ public class XmlTest implements Cloneable { private String m_timeOut; private Boolean m_skipFailedInvocationCounts = XmlSuite.DEFAULT_SKIP_FAILED_INVOCATION_COUNTS; - private Map> m_failedInvocationNumbers = null; // lazily initialized + private final Map> m_failedInvocationNumbers = Maps.newHashMap(); private Boolean m_preserveOrder = XmlSuite.DEFAULT_PRESERVE_ORDER; private int m_index; - private Boolean m_groupByInstances; + private @Nullable Boolean m_groupByInstances; - private Boolean m_allowReturnValues = null; + private @Nullable Boolean m_allowReturnValues = null; private Map m_xmlDependencyGroups = Maps.newHashMap(); @@ -46,14 +47,6 @@ public class XmlTest implements Cloneable { * @param index the index of this test tag in testng.xml */ public XmlTest(XmlSuite suite, int index) { - init(suite, index); - } - - public XmlTest(XmlSuite suite) { - init(suite, 0); - } - - private void init(XmlSuite suite, int index) { m_suite = suite; m_suite.getTests().add(this); m_index = index; @@ -62,7 +55,12 @@ private void init(XmlSuite suite, int index) { m_name = "Default XmlTest name " + UUID.randomUUID(); } + public XmlTest(XmlSuite suite) { + this(suite, 0); + } + // For YAML + @SuppressWarnings("initialization.fields.uninitialized") public XmlTest() {} public void setXmlPackages(List packages) { @@ -104,7 +102,7 @@ public final XmlSuite getSuite() { return m_suite; } - /** @return the includedGroups. */ + /** Returns the includedGroups. */ public List getIncludedGroups() { List result = Lists.newArrayList(); if (m_xmlGroups != null && m_xmlGroups.getRun() != null) { @@ -118,7 +116,7 @@ public boolean isGroupFilteringDisabled() { return getIncludedGroups().isEmpty() && getExcludedGroups().isEmpty(); } - /** @return Returns the classes. */ + /** Returns the classes. */ public List getXmlClasses() { return m_xmlClasses; } @@ -142,7 +140,7 @@ public void setXmlClasses(List classes) { m_xmlClasses = classes; } - /** @return Returns the name. */ + /** Returns the name. */ public String getName() { return m_name; } @@ -216,7 +214,7 @@ public void addExcludedGroup(String g) { m_xmlGroups.getRun().getExcludes().add(g); } - /** @return Returns the verbose. */ + /** Returns the verbose. */ public int getVerbose() { Integer result = m_verbose; if (null == result || XmlSuite.DEFAULT_VERBOSE.equals(m_verbose)) { @@ -231,7 +229,7 @@ public int getVerbose() { } public boolean getGroupByInstances() { - Boolean result = m_groupByInstances; + @Nullable Boolean result = m_groupByInstances; if (result == null || XmlSuite.DEFAULT_GROUP_BY_INSTANCES.equals(m_groupByInstances)) { result = getSuite().getGroupByInstances(); } @@ -270,7 +268,7 @@ public void setSkipFailedInvocationCounts(boolean skip) { m_skipFailedInvocationCounts = skip; } - /** @return Returns the isJUnit. */ + /** Returns the isJUnit. */ public boolean skipFailedInvocationCounts() { Boolean result = m_skipFailedInvocationCounts; if (null == result) { @@ -301,7 +299,7 @@ public void setMetaGroups(Map> metaGroups) { } } - /** @return Returns the metaGroups. */ + /** Returns the metaGroups. */ public Map> getMetaGroups() { if (m_xmlGroups == null) { return Collections.emptyMap(); @@ -323,8 +321,8 @@ public void addParameter(String key, String value) { m_parameters.put(key, value); } - public String getParameter(String name) { - String result = m_parameters.get(name); + public @Nullable String getParameter(String name) { + @Nullable String result = m_parameters.get(name); if (null == result) { result = getSuite().getParameter(name); } @@ -332,7 +330,7 @@ public String getParameter(String name) { return result; } - /** @return the parameters defined in this test tag and the tags above it. */ + /** Returns the parameters defined in this test tag and the tags above it. */ public Map getAllParameters() { Map result = Maps.newHashMap(); result.putAll(getSuite().getParameters()); @@ -341,8 +339,8 @@ public Map getAllParameters() { } /** - * @return the parameters defined in this tag, and only this test tag. To retrieve the inherited - * parameters as well, call {@code getAllParameters()}. + * Returns the parameters defined in this tag, and only this test tag. To retrieve the inherited + * parameters as well, call {@code getAllParameters()}. */ public Map getLocalParameters() { return m_parameters; @@ -356,8 +354,8 @@ public XmlSuite.ParallelMode getParallel() { return Optional.ofNullable(m_parallel).orElse(getSuite().getParallel()); } - public String getTimeOut() { - String result = getSuite().getTimeOut(); + public @Nullable String getTimeOut() { + @Nullable String result = getSuite().getTimeOut(); if (null != m_timeOut) { result = m_timeOut; } @@ -367,8 +365,9 @@ public String getTimeOut() { public long getTimeOut(long def) { long result = def; - if (getTimeOut() != null) { - result = Long.parseLong(getTimeOut()); + @Nullable String timeOut = getTimeOut(); + if (timeOut != null) { + result = Long.parseLong(timeOut); } return result; @@ -378,7 +377,7 @@ public void setTimeOut(long timeOut) { m_timeOut = Long.toString(timeOut); } - private void setTimeOut(String timeOut) { + private void setTimeOut(@Nullable String timeOut) { m_timeOut = timeOut; } @@ -394,7 +393,7 @@ public void setScript(XmlScript script) { } } - public XmlScript getScript() { + public @Nullable XmlScript getScript() { List selectors = getMethodSelectors(); if (selectors.isEmpty()) { return null; @@ -440,6 +439,22 @@ public Object clone() { return result; } + private Map> getFailedInvocationNumbers() { + if (!m_failedInvocationNumbers.isEmpty()) { + return m_failedInvocationNumbers; + } + for (XmlClass c : getXmlClasses()) { + for (XmlInclude xi : c.getIncludedMethods()) { + List invocationNumbers = xi.getInvocationNumbers(); + if (!invocationNumbers.isEmpty()) { + String methodName = c.getName() + "." + xi.getName(); + m_failedInvocationNumbers.put(methodName, invocationNumbers); + } + } + } + return m_failedInvocationNumbers; + } + /** * Convenience method to cache the ordering numbers for methods. * @@ -447,20 +462,7 @@ public Object clone() { * @return The invocation numbers of the method */ public List getInvocationNumbers(String method) { - if (m_failedInvocationNumbers == null) { - m_failedInvocationNumbers = Maps.newHashMap(); - for (XmlClass c : getXmlClasses()) { - for (XmlInclude xi : c.getIncludedMethods()) { - List invocationNumbers = xi.getInvocationNumbers(); - if (invocationNumbers.size() > 0) { - String methodName = c.getName() + "." + xi.getName(); - m_failedInvocationNumbers.put(methodName, invocationNumbers); - } - } - } - } - - List result = m_failedInvocationNumbers.get(method); + @Nullable List result = getFailedInvocationNumbers().get(method); if (result == null) { // Don't use emptyList here since this list might end up receiving values if // the test run fails. @@ -548,15 +550,15 @@ public int hashCode() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } - if (obj == null) return XmlSuite.f(); - if (getClass() != obj.getClass()) return XmlSuite.f(); + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; XmlTest other = (XmlTest) obj; if (m_xmlGroups == null) { - if (other.m_xmlGroups != null) return XmlSuite.f(); + if (other.m_xmlGroups != null) return false; } else { if (other.m_xmlGroups == null) { return false; @@ -566,55 +568,53 @@ public boolean equals(Object obj) { return false; } if (!m_xmlGroups.getRun().getExcludes().equals(other.m_xmlGroups.getRun().getExcludes())) { - return XmlSuite.f(); + return false; } if (!m_xmlGroups.getRun().getIncludes().equals(other.m_xmlGroups.getRun().getIncludes())) { - return XmlSuite.f(); + return false; } if (!m_xmlGroups.getDefines().equals(other.m_xmlGroups.getDefines())) { return false; } } if (m_failedInvocationNumbers == null) { - if (other.m_failedInvocationNumbers != null) return XmlSuite.f(); - } else if (!m_failedInvocationNumbers.equals(other.m_failedInvocationNumbers)) - return XmlSuite.f(); + if (other.m_failedInvocationNumbers != null) return false; + } else if (!m_failedInvocationNumbers.equals(other.m_failedInvocationNumbers)) return false; if (m_isJUnit == null) { - if (other.m_isJUnit != null && !other.m_isJUnit.equals(XmlSuite.DEFAULT_JUNIT)) - return XmlSuite.f(); - } else if (!m_isJUnit.equals(other.m_isJUnit)) return XmlSuite.f(); + if (other.m_isJUnit != null && !other.m_isJUnit.equals(XmlSuite.DEFAULT_JUNIT)) return false; + } else if (!m_isJUnit.equals(other.m_isJUnit)) return false; if (m_methodSelectors == null) { - if (other.m_methodSelectors != null) return XmlSuite.f(); - } else if (!m_methodSelectors.equals(other.m_methodSelectors)) return XmlSuite.f(); + if (other.m_methodSelectors != null) return false; + } else if (!m_methodSelectors.equals(other.m_methodSelectors)) return false; if (m_name == null) { - if (other.m_name != null) return XmlSuite.f(); - } else if (!m_name.equals(other.m_name)) return XmlSuite.f(); + if (other.m_name != null) return false; + } else if (!m_name.equals(other.m_name)) return false; if (m_parallel == null) { - if (other.m_parallel != null) return XmlSuite.f(); - } else if (!m_parallel.equals(other.m_parallel)) return XmlSuite.f(); + if (other.m_parallel != null) return false; + } else if (!m_parallel.equals(other.m_parallel)) return false; if (m_parameters == null) { - if (other.m_parameters != null) return XmlSuite.f(); - } else if (!m_parameters.equals(other.m_parameters)) return XmlSuite.f(); + if (other.m_parameters != null) return false; + } else if (!m_parameters.equals(other.m_parameters)) return false; if (m_preserveOrder == null) { - if (other.m_preserveOrder != null) return XmlSuite.f(); - } else if (!m_preserveOrder.equals(other.m_preserveOrder)) return XmlSuite.f(); + if (other.m_preserveOrder != null) return false; + } else if (!m_preserveOrder.equals(other.m_preserveOrder)) return false; if (m_skipFailedInvocationCounts == null) { - if (other.m_skipFailedInvocationCounts != null) return XmlSuite.f(); + if (other.m_skipFailedInvocationCounts != null) return false; } else if (!m_skipFailedInvocationCounts.equals(other.m_skipFailedInvocationCounts)) - return XmlSuite.f(); - if (m_threadCount != other.m_threadCount) return XmlSuite.f(); + return false; + if (m_threadCount != other.m_threadCount) return false; if (m_timeOut == null) { - if (other.m_timeOut != null) return XmlSuite.f(); - } else if (!m_timeOut.equals(other.m_timeOut)) return XmlSuite.f(); + if (other.m_timeOut != null) return false; + } else if (!m_timeOut.equals(other.m_timeOut)) return false; if (m_verbose == null) { - if (other.m_verbose != null) return XmlSuite.f(); - } else if (!m_verbose.equals(other.m_verbose)) return XmlSuite.f(); + if (other.m_verbose != null) return false; + } else if (!m_verbose.equals(other.m_verbose)) return false; if (m_xmlClasses == null) { - if (other.m_xmlClasses != null) return XmlSuite.f(); - } else if (!m_xmlClasses.equals(other.m_xmlClasses)) return XmlSuite.f(); + if (other.m_xmlClasses != null) return false; + } else if (!m_xmlClasses.equals(other.m_xmlClasses)) return false; if (m_xmlPackages == null) { - if (other.m_xmlPackages != null) return XmlSuite.f(); - } else if (!m_xmlPackages.equals(other.m_xmlPackages)) return XmlSuite.f(); + if (other.m_xmlPackages != null) return false; + } else if (!m_xmlPackages.equals(other.m_xmlPackages)) return false; return true; } @@ -662,8 +662,9 @@ public XmlGroups getXmlGroups() { } /** + * Returns true if the current test's name matches with any of the given names. + * * @param names The list of names to check. - * @return true if the current test's name matches with any of the given names. */ public boolean nameMatchesAny(List names) { return names.contains(getName()); diff --git a/testng-core/src/main/java/org/testng/package-info.java b/testng-core/src/main/java/org/testng/package-info.java new file mode 100644 index 0000000000..bf86fddb16 --- /dev/null +++ b/testng-core/src/main/java/org/testng/package-info.java @@ -0,0 +1,6 @@ +@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.ALL) +package org.testng; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.checkerframework.framework.qual.TypeUseLocation; diff --git a/testng-reflection-utils/src/main/java/org/testng/internal/reflect/ReflectionHelper.java b/testng-reflection-utils/src/main/java/org/testng/internal/reflect/ReflectionHelper.java index 7351044c0f..7524774257 100644 --- a/testng-reflection-utils/src/main/java/org/testng/internal/reflect/ReflectionHelper.java +++ b/testng-reflection-utils/src/main/java/org/testng/internal/reflect/ReflectionHelper.java @@ -3,18 +3,20 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.Nullable; import org.testng.collections.Lists; -public class ReflectionHelper { +public final class ReflectionHelper { + private ReflectionHelper() {} /** - * @return An array of all locally declared methods or equivalent thereof (such as default methods - * on Java 8 based interfaces that the given class implements). + * Returns an array of all locally declared methods or equivalent thereof (such as default methods + * on Java 8 based interfaces that the given class implements). */ public static Method[] getLocalMethods(Class clazz) { Method[] declaredMethods = excludingMain(clazz); @@ -39,13 +41,13 @@ public static Method[] getLocalMethods(Class clazz) { } /** - * @return An array of all locally declared methods or equivalent thereof (such as default methods - * on Java 8 based interfaces that the given class implements) but excludes the main() + * Returns an array of all locally declared methods or equivalent thereof (such as default methods + * on Java 8 based interfaces that the given class implements) but excludes the main() * method alone. */ public static Method[] excludingMain(Class clazz) { Method[] declaredMethods = clazz.getDeclaredMethods(); - List pruned = new LinkedList<>(); + List pruned = new ArrayList<>(); for (Method declaredMethod : declaredMethods) { if ("main".equals(declaredMethod.getName()) && isStaticVoid(declaredMethod) @@ -66,13 +68,13 @@ && acceptsStringArray(declaredMethod)) { * @param - The annotation type * @return - Either the annotation if found (or) null. */ - public static T findAnnotation( + public static @Nullable T findAnnotation( Class typedTestClass, Class annotation) { if (typedTestClass == null || annotation == null) { return null; } - T ignore = null; - Class testClass = typedTestClass; + @Nullable T ignore = null; + @Nullable Class testClass = typedTestClass; while (testClass != null && testClass != Object.class) { ignore = testClass.getAnnotation(annotation); @@ -106,9 +108,10 @@ private static List getDefaultMethods(Class clazz) { private static Set> getAllInterfaces(Class clazz) { Set> result = new HashSet<>(); - while (clazz != null && clazz != Object.class) { - result.addAll(Arrays.asList(clazz.getInterfaces())); - clazz = clazz.getSuperclass(); + @Nullable Class superClass = clazz; + while (superClass != null && superClass != Object.class) { + result.addAll(Arrays.asList(superClass.getInterfaces())); + superClass = superClass.getSuperclass(); } return result; } diff --git a/testng-reflection-utils/src/main/java/org/testng/internal/reflect/package-info.java b/testng-reflection-utils/src/main/java/org/testng/internal/reflect/package-info.java new file mode 100644 index 0000000000..cf7cac4b26 --- /dev/null +++ b/testng-reflection-utils/src/main/java/org/testng/internal/reflect/package-info.java @@ -0,0 +1,6 @@ +@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.ALL) +package org.testng.internal.reflect; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.checkerframework.framework.qual.TypeUseLocation; diff --git a/testng-reflection-utils/testng-reflection-utils-build.gradle.kts b/testng-reflection-utils/testng-reflection-utils-build.gradle.kts index 17304ed1b6..a4f83cda8b 100644 --- a/testng-reflection-utils/testng-reflection-utils-build.gradle.kts +++ b/testng-reflection-utils/testng-reflection-utils-build.gradle.kts @@ -4,4 +4,5 @@ plugins { dependencies { implementation(projects.testngCollections) + implementation("org.checkerframework:checker-qual:3.36.0") } diff --git a/testng-runner-api/src/main/java/org/testng/internal/package-info.java b/testng-runner-api/src/main/java/org/testng/internal/package-info.java new file mode 100644 index 0000000000..b113aab45c --- /dev/null +++ b/testng-runner-api/src/main/java/org/testng/internal/package-info.java @@ -0,0 +1,6 @@ +@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.ALL) +package org.testng.internal; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.checkerframework.framework.qual.TypeUseLocation; diff --git a/testng-test-kit/src/main/java/testhelper/PerformanceUtils.java b/testng-test-kit/src/main/java/testhelper/PerformanceUtils.java index 6a804f63dd..b057e22127 100644 --- a/testng-test-kit/src/main/java/testhelper/PerformanceUtils.java +++ b/testng-test-kit/src/main/java/testhelper/PerformanceUtils.java @@ -4,13 +4,14 @@ import java.lang.management.ThreadMXBean; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import org.checkerframework.checker.nullness.qual.Nullable; public class PerformanceUtils { private static final ThreadMXBean THREAD_MX_BEAN = ManagementFactory.getThreadMXBean(); - private static final Method GET_THREAD_ALLOCATED_BYTES = initGetThreadAllocatedBytes(); + private static final @Nullable Method GET_THREAD_ALLOCATED_BYTES = initGetThreadAllocatedBytes(); - private static Method initGetThreadAllocatedBytes() { + private static @Nullable Method initGetThreadAllocatedBytes() { try { Method method = THREAD_MX_BEAN.getClass().getMethod("getThreadAllocatedBytes", long.class); method.setAccessible(true); @@ -25,7 +26,7 @@ public static boolean canMeasureAllocatedMemory() { return GET_THREAD_ALLOCATED_BYTES != null; } - /** @return amount of memory (in bytes) allocated by current thread until now */ + /** Returns amount of memory (in bytes) allocated by current thread until now. */ public static long measureAllocatedMemory() { if (GET_THREAD_ALLOCATED_BYTES == null) { throw new IllegalStateException( @@ -34,7 +35,12 @@ public static long measureAllocatedMemory() { long selfId = Thread.currentThread().getId(); try { - return (long) GET_THREAD_ALLOCATED_BYTES.invoke(THREAD_MX_BEAN, selfId); + Object result = GET_THREAD_ALLOCATED_BYTES.invoke(THREAD_MX_BEAN, selfId); + if (!(result instanceof Long)) { + throw new IllegalStateException( + "Method getThreadAllocatedBytes(long) returns unexpected type: " + result); + } + return (Long) result; } catch (IllegalAccessException e) { throw new RuntimeException("Unable to call " + GET_THREAD_ALLOCATED_BYTES, e); } catch (InvocationTargetException e) { diff --git a/testng-test-kit/testng-test-kit-build.gradle.kts b/testng-test-kit/testng-test-kit-build.gradle.kts index 0438ec3bf1..65a3160e70 100644 --- a/testng-test-kit/testng-test-kit-build.gradle.kts +++ b/testng-test-kit/testng-test-kit-build.gradle.kts @@ -1,3 +1,7 @@ plugins { id("testng.java-library") } + +dependencies { + implementation("org.checkerframework:checker-qual:3.36.0") +} diff --git a/versions.properties b/versions.properties index 9a7016ba18..7e765dbdde 100644 --- a/versions.properties +++ b/versions.properties @@ -7,6 +7,8 @@ #### suppress inspection "SpellCheckingInspection" for whole file #### suppress inspection "UnusedProperty" for whole file +plugin.org.checkerframework=0.6.29 + plugin.com.github.vlsi.stage-vote-release=1.78 version.ch.qos.logback..logback-classic=1.2.11