From 10a2687ff19722f4076c28227957b71418868aad Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Dec 2021 21:08:45 +0100 Subject: [PATCH] [sinttest] Allow the selection of individual test *methods* --- documentation/developer/integrationtest.md | 21 +++ .../smack/util/CollectionUtil.java | 7 + .../smack/inttest/Configuration.java | 124 +++++++++++++++++- .../SmackIntegrationTestFramework.java | 18 +-- 4 files changed, 153 insertions(+), 17 deletions(-) diff --git a/documentation/developer/integrationtest.md b/documentation/developer/integrationtest.md index 60f3142b8..d351e71e1 100644 --- a/documentation/developer/integrationtest.md +++ b/documentation/developer/integrationtest.md @@ -86,6 +86,27 @@ debugger=console The framework will first load the properties file from `~/.config/smack-integration-test/properties` +### Running selected tests only + +Using `enabledTests` is is possible to run only selected tests. The +tests can be selected on a per class base or by specifying concrete +test methods. In the latter case, the methods must be qualified by a +(simple) class name. + +For example: + +```bash +$ gradle integrationTest -Dsinttest.enabledTests=SoftwareInfoIntegrationTest.test +``` + +will only run the `test()` method of `SoftwareInfoIntegrationTest`, whereas + +```bash +$ gradle integrationTest -Dsinttest.enabledTests=SoftwareInfoIntegrationTest +``` + +would run all tests defined in the `SoftwareInfoIntegrationTest` class. + Overview of the components -------------------------- diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java index 24b73b8b0..408e00bfd 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java @@ -89,4 +89,11 @@ public class CollectionUtil { } return Collections.singletonList(element); } + + public static Set nullSafeUnmodifiableSet(Set set) { + if (set == null) { + return Collections.emptySet(); + } + return Collections.unmodifiableSet(set); + } } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java index e865fb5db..700c12eff 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2020 Florian Schmaus + * Copyright 2015-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,11 @@ package org.igniterealtime.smack.inttest; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.lang.reflect.Method; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -33,6 +36,7 @@ import javax.net.ssl.SSLContext; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; import org.jivesoftware.smack.debugger.ConsoleDebugger; +import org.jivesoftware.smack.util.CollectionUtil; import org.jivesoftware.smack.util.Function; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.ParserUtils; @@ -101,8 +105,12 @@ public final class Configuration { public final Set enabledTests; + private final Map> enabledTestsMap; + public final Set disabledTests; + private final Map> disabledTestsMap; + public final String defaultConnectionNickname; public final Set enabledConnections; @@ -169,8 +177,10 @@ public final class Configuration { this.accountTwoPassword = builder.accountTwoPassword; this.accountThreeUsername = builder.accountThreeUsername; this.accountThreePassword = builder.accountThreePassword; - this.enabledTests = builder.enabledTests; - this.disabledTests = builder.disabledTests; + this.enabledTests = CollectionUtil.nullSafeUnmodifiableSet(builder.enabledTests); + this.enabledTestsMap = convertTestsToMap(enabledTests); + this.disabledTests = CollectionUtil.nullSafeUnmodifiableSet(builder.disabledTests); + this.disabledTestsMap = convertTestsToMap(disabledTests); this.defaultConnectionNickname = builder.defaultConnectionNickname; this.enabledConnections = builder.enabledConnections; this.disabledConnections = builder.disabledConnections; @@ -577,4 +587,112 @@ public final class Configuration { }); } + private static Map> convertTestsToMap(Set tests) { + Map> res = new HashMap<>(); + for (String test : tests) { + String[] testParts = test.split("\\."); + if (testParts.length == 1) { + // The whole test specification does not contain a dot, assume it is a test class specification. + res.put(test, Collections.emptySet()); + continue; + } + + String lastTestPart = testParts[testParts.length - 1]; + if (lastTestPart.isEmpty()) { + throw new IllegalArgumentException("Invalid test specifier: " + test); + } + + char firstCharOfLastTestPart = lastTestPart.charAt(0); + if (!Character.isLowerCase(firstCharOfLastTestPart)) { + // The first character of the last test part is not lowercase, assume this is a fully qualified test + // class specification, e.g. org.foo.bar.TestClass. + res.put(test, Collections.emptySet()); + } + + // The first character of the last test part is lowercase, assume this is a test class *and* method name + // specification. + String testMethodName = lastTestPart; + int classPartsCount = testParts.length - 1; + String[] classParts = new String[classPartsCount]; + System.arraycopy(testParts, 0, classParts, 0, classPartsCount); + String testClass = String.join(".", classParts); + + res.compute(testClass, (k, v) -> { + if (v == null) { + v = new HashSet<>(); + } + v.add(testMethodName); + return v; + }); + } + return res; + } + + private static Set getKey(Class testClass, Map> testsMap) { + String className = testClass.getName(); + if (testsMap.containsKey(className)) { + return testsMap.get(className); + } + + String unqualifiedClassName = testClass.getSimpleName(); + if (testsMap.containsKey(unqualifiedClassName)) { + return testsMap.get(unqualifiedClassName); + } + + return null; + } + + private static boolean contains(Class testClass, Map> testsMap) { + Set enabledMethods = getKey(testClass, testsMap); + return enabledMethods != null; + } + + public boolean isClassEnabled(Class testClass) { + if (enabledTestsMap.isEmpty()) { + return true; + } + + return contains(testClass, enabledTestsMap); + } + + public boolean isClassDisabled(Class testClass) { + if (disabledTestsMap.isEmpty()) { + return false; + } + + return contains(testClass, disabledTestsMap); + } + + private static boolean contains(Method method, Map> testsMap) { + Class testClass = method.getDeclaringClass(); + Set methods = getKey(testClass, testsMap); + + if (methods == null) { + return false; + } + + if (methods.isEmpty()) { + return true; + } + + String methodName = method.getName(); + return methods.contains(methodName); + } + + public boolean isMethodEnabled(Method method) { + if (enabledTestsMap.isEmpty()) { + return true; + } + + return contains(method, enabledTestsMap); + } + + public boolean isMethodDisabled(Method method) { + if (disabledTestsMap.isEmpty()) { + return false; + } + + return contains(method, disabledTestsMap); + } + } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java index 769146c6f..433386600 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java @@ -282,13 +282,13 @@ public class SmackIntegrationTestFramework { continue; } - if (config.enabledTests != null && !isInSet(testClass, config.enabledTests)) { + if (!config.isClassEnabled(testClass)) { DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is not enabled"); testRunResult.disabledTestClasses.add(disabledTestClass); continue; } - if (isInSet(testClass, config.disabledTests)) { + if (config.isClassDisabled(testClass)) { DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is disalbed"); testRunResult.disabledTestClasses.add(disabledTestClass); continue; @@ -377,14 +377,13 @@ public class SmackIntegrationTestFramework { while (it.hasNext()) { final Method method = it.next(); final String methodName = method.getName(); - if (config.enabledTests != null && !(config.enabledTests.contains(methodName) - || isInSet(testClass, config.enabledTests))) { + if (!config.isMethodEnabled(method)) { DisabledTest disabledTest = new DisabledTest(method, "Skipping test method " + methodName + " because it is not enabled"); testRunResult.disabledTests.add(disabledTest); it.remove(); continue; } - if (config.disabledTests != null && config.disabledTests.contains(methodName)) { + if (config.isMethodDisabled(method)) { DisabledTest disabledTest = new DisabledTest(method, "Skipping test method " + methodName + " because it is disabled"); testRunResult.disabledTests.add(disabledTest); it.remove(); @@ -607,15 +606,6 @@ public class SmackIntegrationTestFramework { return (Exception) e; } - private static boolean isInSet(Class clz, Set classes) { - if (classes == null) { - return false; - } - final String className = clz.getName(); - final String unqualifiedClassName = clz.getSimpleName(); - return classes.contains(className) || classes.contains(unqualifiedClassName); - } - public static final class TestRunResult { /**