From a91cdc38f655fe10c86e63cc8da68986e0662726 Mon Sep 17 00:00:00 2001 From: srafi Date: Thu, 29 Aug 2024 09:54:13 -0400 Subject: [PATCH 1/2] IT file changes- TestFramework.scala, Runner.scala, RunnerFactory.java --- .../data/framework/TestFramework.scala | 144 ++++++++++++------ .../cs/testrunner/runner/Runner.scala | 76 +++++---- .../cs/testrunner/runner/RunnerFactory.java | 32 +++- 3 files changed, 172 insertions(+), 80 deletions(-) diff --git a/testrunner-running/src/main/scala/edu/illinois/cs/testrunner/data/framework/TestFramework.scala b/testrunner-running/src/main/scala/edu/illinois/cs/testrunner/data/framework/TestFramework.scala index 215ce39..c9e39ba 100644 --- a/testrunner-running/src/main/scala/edu/illinois/cs/testrunner/data/framework/TestFramework.scala +++ b/testrunner-running/src/main/scala/edu/illinois/cs/testrunner/data/framework/TestFramework.scala @@ -13,6 +13,7 @@ import java.util.List; import scala.collection.JavaConverters._ import scala.collection.mutable.ListBuffer import scala.util.Try +import java.util.logging.{Level, Logger} object TestFramework { def testFramework(project: ProjectWrapper): Option[TestFramework] = { @@ -106,98 +107,145 @@ trait TestFramework { } object JUnit extends TestFramework { - val methodAnnotationStr: String = "org.junit.Test" + val methodAnnotationStr: String = "org.junit.Test" + + override def tryGenerateTestClass(loader: ClassLoader, clzName: String): Option[GeneralTestClass] = { - override def tryGenerateTestClass(loader: ClassLoader, clzName: String) - : Option[GeneralTestClass] = { - val annotation: Class[_ <: Annotation] = + val annotation: Class[_ <: Annotation] = loader.loadClass(methodAnnotationStr).asInstanceOf[Class[_ <: Annotation]] try { + val clz = loader.loadClass(clzName) + Logger.getGlobal().log(Level.INFO, s"Loaded class: $clzName") + if (!Modifier.isAbstract(clz.getModifiers)) { + val methods = clz.getMethods.toStream - Try(if (methods.exists(_.getAnnotation(annotation) != null)) { + // Check if it is an integration test by name or directory + + val isIntegrationTestByName = clzName.endsWith("IT") + val classFilePath = clz.getProtectionDomain.getCodeSource.getLocation.getPath + val isIntegrationTestByDirectory = classFilePath.contains("/target/test-classes/") + + Logger.getGlobal().log(Level.INFO, s"Class $clzName integration test by name: $isIntegrationTestByName") + Logger.getGlobal().log(Level.INFO, s"Class $clzName integration test by directory: $isIntegrationTestByDirectory") + + Try(if (methods.exists(_.getAnnotation(annotation) != null) || + (isIntegrationTestByName && isIntegrationTestByDirectory)) { + Logger.getGlobal().log(Level.INFO, s"Class $clzName recognized as a test class.") Option(new JUnitTestClass(loader, clz)) } else if (loader.loadClass("junit.framework.TestCase").isAssignableFrom(clz)) { + Logger.getGlobal().log(Level.INFO, s"Class $clzName recognized as a TestCase class.") Option(new JUnitTestCaseClass(loader, clz)) } else { + Logger.getGlobal().log(Level.INFO, s"Class $clzName is not a test class, skipping.") Option.empty }).toOption.flatten + } else { + Logger.getGlobal().log(Level.INFO, s"Class $clzName is abstract, skipping.") Option.empty + } + } catch { - case e: NoClassDefFoundError => Option.empty + + case e: NoClassDefFoundError => + Logger.getGlobal().log(Level.WARNING, s"Class $clzName could not be loaded due to NoClassDefFoundError.") + Option.empty + } + } override def toString(): String = "JUnit" - - // the splitor to split the class name and method name - // for the full qualified name of JUnit 4 test + // the delimiter to split the class name and method name + // for the fully qualified name of JUnit 4 test // like com.package.JUnit4TestClass.TestA override def getDelimiter(): String = "." + } object JUnit5 extends TestFramework { + val methodAnnotationStr: String = "org.junit.jupiter.api.Test" val nestedAnnotationStr: String = "org.junit.jupiter.api.Nested" val disabledAnnotationStr: String = "org.junit.jupiter.api.Disabled" - override def tryGenerateTestClass(loader: ClassLoader, clzName: String) - : Option[GeneralTestClass] = { - val testAnnotation: Class[_ <: Annotation] = - loader.loadClass(methodAnnotationStr).asInstanceOf[Class[_ <: Annotation]] - val disabledAnnotation: Class[_ <: Annotation] = - loader.loadClass(disabledAnnotationStr).asInstanceOf[Class[_ <: Annotation]] + override def tryGenerateTestClass(loader: ClassLoader, clzName: String): Option[GeneralTestClass] = { - try { - val clz = loader.loadClass(clzName) + val testAnnotation: Class[_ <: Annotation] = + loader.loadClass(methodAnnotationStr).asInstanceOf[Class[_ <: Annotation]] + + val disabledAnnotation: Class[_ <: Annotation] = + loader.loadClass(disabledAnnotationStr).asInstanceOf[Class[_ <: Annotation]] + + + try { + + val clz = loader.loadClass(clzName) + Logger.getGlobal().log(Level.INFO, s"Loaded class: $clzName") + + if (clz.getAnnotation(disabledAnnotation) != null) { + // Skip disabled class + Logger.getGlobal().log(Level.INFO, s"Class $clzName is disabled, skipping.") + return Option.empty + + } - if (clz.getAnnotation(disabledAnnotation) != null) { - // skip disabled class + if (clz.isMemberClass) { + val nestedAnnotation: Class[_ <: Annotation] = + loader.loadClass(nestedAnnotationStr).asInstanceOf[Class[_ <: Annotation]] + + if (Modifier.isStatic(clz.getModifiers) || + clz.getAnnotation(nestedAnnotation) == null) { + // Skip unqualified nested test class + Logger.getGlobal().log(Level.INFO, s"Class $clzName is an unqualified nested test class, skipping.") return Option.empty } - if (clz.isMemberClass) { - val nestedAnnotation: Class[_ <: Annotation] = - loader.loadClass(nestedAnnotationStr) - .asInstanceOf[Class[_ <: Annotation]] - if (Modifier.isStatic(clz.getModifiers) || - clz.getAnnotation(nestedAnnotation) == null) { - // a nested test class should - // (1) be non-static - // (2) have @Nested annotation - // - // Skip unqualified test class - return Option.empty - } - } + } - if (!Modifier.isAbstract(clz.getModifiers)) { - val methods = Utility.getAllMethods(clz) + // Check if it is an integration test by name or directory + + val isIntegrationTestByName = clzName.endsWith("IT") + val classFilePath = clz.getProtectionDomain.getCodeSource.getLocation.getPath + val isIntegrationTestByDirectory = classFilePath.contains("/target/test-classes/") + + Logger.getGlobal().log(Level.INFO, s"Class $clzName integration test by name: $isIntegrationTestByName") + Logger.getGlobal().log(Level.INFO, s"Class $clzName integration test by directory: $isIntegrationTestByDirectory") + + + if (!Modifier.isAbstract(clz.getModifiers) && + + (clz.getDeclaredMethods.exists(_.getAnnotation(testAnnotation) != null) || (isIntegrationTestByName && isIntegrationTestByDirectory))) { + Logger.getGlobal().log(Level.INFO, s"Class $clzName recognized as a test class.") + Option(new JUnit5TestClass(loader, clz)) + + } else { + + Logger.getGlobal().log(Level.INFO, s"Class $clzName is not a test class, skipping.") + Option.empty - Try(if (methods.exists(_.getAnnotation(testAnnotation) != null)) { - Option(new JUnit5TestClass(loader, clz)) - } else { - Option.empty - }).toOption.flatten - } else { - Option.empty - } - } catch { - case e: NoClassDefFoundError => Option.empty } - } - override def toString(): String = "JUnit5" + } catch { + + case e: NoClassDefFoundError => + Logger.getGlobal().log(Level.WARNING, s"Class $clzName could not be loaded due to NoClassDefFoundError.") + Option.empty + } + + } + override def toString(): String = "JUnit5" + // the splitor to split the class name and method name // for the full qualified name of JUnit 5 test // like com.package.JUnit5TestClass#TestA override def getDelimiter(): String = "#" -} \ No newline at end of file +} diff --git a/testrunner-running/src/main/scala/edu/illinois/cs/testrunner/runner/Runner.scala b/testrunner-running/src/main/scala/edu/illinois/cs/testrunner/runner/Runner.scala index c48274d..bc6b75d 100644 --- a/testrunner-running/src/main/scala/edu/illinois/cs/testrunner/runner/Runner.scala +++ b/testrunner-running/src/main/scala/edu/illinois/cs/testrunner/runner/Runner.scala @@ -16,6 +16,8 @@ import scala.collection.mutable.ListBuffer import scala.io.Source import scala.util.{Failure, Try} +import java.util.logging.{Level, Logger} + trait Runner { def outputPath(): Path def classpath(): String @@ -56,36 +58,56 @@ trait Runner { def generateTestRunId(): String = System.currentTimeMillis() + "-" + UUID.randomUUID.toString - def runWithCp(cp: String, testOrder: Stream[String]): Try[TestRunResult] = - TempFiles.withSeq(testOrder)(path => - TempFiles.withTempFile(outputPath => - TempFiles.withProperties(Configuration.config().properties())(propertiesPath => { - val builder = makeBuilder(cp + File.pathSeparator + Configuration.config().getProperty("testplugin.classpath")) - - val info = execution(testOrder, builder) - - val testRunId = generateTestRunId() - - val exitCode = info.run( - testRunId, - framework().toString, - path.toAbsolutePath.toString, - propertiesPath.toAbsolutePath.toString, - outputPath.toAbsolutePath.toString).exitValue() - - if (exitCode == 0) { - autoClose(Source.fromFile(outputPath.toAbsolutePath.toString).bufferedReader())(reader => - Try(new Gson().fromJson(reader, classOf[TestRunResult]))) - } else { - // Try to copy the output log so that it can be inspected - val failureLog = Paths.get("failing-test-output-" + testRunId) - Files.copy(info.outputPath, failureLog, StandardCopyOption.REPLACE_EXISTING) - Failure(new Exception("Non-zero exit code (output in " + failureLog.toAbsolutePath + "): " ++ exitCode.toString)) - } - }))).flatten.flatten.flatten.flatten + val logger = Logger.getLogger(getClass.getName) + + def runWithCp(cp: String, testOrder: Stream[String]): Try[TestRunResult] = { + logger.log(Level.INFO, "runWithCp method is being executed") + + + TempFiles.withSeq(testOrder)(path => { + println(s"Temporary path created: $path") // Print after creating the sequence of test paths + + TempFiles.withTempFile(outputPath => { + println(s"Temporary output path created: $outputPath") // Print after creating the temporary output file + + TempFiles.withProperties(Configuration.config().properties())(propertiesPath => { + println(s"Properties path created: $propertiesPath") // Print after creating the properties path + + val builder = makeBuilder(cp + File.pathSeparator + Configuration.config().getProperty("testplugin.classpath")) + + val info = execution(testOrder, builder) + + val testRunId = generateTestRunId() + println(s"Generated testRunId: $testRunId") // Print the generated testRunId + + val exitCode = info.run( + testRunId, + framework().toString, + path.toAbsolutePath.toString, + propertiesPath.toAbsolutePath.toString, + outputPath.toAbsolutePath.toString).exitValue() + + println(s"Execution finished with exit code: $exitCode") // Print the exit code + + if (exitCode == 0) { + autoClose(Source.fromFile(outputPath.toAbsolutePath.toString).bufferedReader())(reader => + Try(new Gson().fromJson(reader, classOf[TestRunResult]))) + } else { + // Try to copy the output log so that it can be inspected + val failureLog = Paths.get("failing-test-output-" + testRunId) + Files.copy(info.outputPath, failureLog, StandardCopyOption.REPLACE_EXISTING) + println(s"Non-zero exit code. Output copied to: $failureLog") // Print the failure log location + Failure(new Exception("Non-zero exit code (output in " + failureLog.toAbsolutePath + "): " ++ exitCode.toString)) + } + }) + }) + }).flatten.flatten.flatten.flatten + } } + trait RunnerProvider[A <: Runner] { def withFramework(framework: TestFramework, classpath: String, environment: java.util.Map[String, String], outputPath: Path): A } + diff --git a/testrunner-running/src/main/scala/edu/illinois/cs/testrunner/runner/RunnerFactory.java b/testrunner-running/src/main/scala/edu/illinois/cs/testrunner/runner/RunnerFactory.java index ccb2dce..104399e 100644 --- a/testrunner-running/src/main/scala/edu/illinois/cs/testrunner/runner/RunnerFactory.java +++ b/testrunner-running/src/main/scala/edu/illinois/cs/testrunner/runner/RunnerFactory.java @@ -16,6 +16,9 @@ import java.util.stream.Stream; import scala.Option; +import java.util.logging.Logger; +import java.util.logging.Level; + public class RunnerFactory { public static Option from(final ProjectWrapper project) { return TestFramework.testFramework(project) @@ -44,11 +47,29 @@ public static Option from(final MavenProject project) { } public static List allFrom(final MavenProject project) { - return TestFramework.getListOfFrameworks(project).stream() - .map(framework -> - create(framework, new ProjectClassLoader(project).classpath(), - surefireEnvironment(project), project.getBasedir().toPath())) - .collect(Collectors.toList()); + System.out.println("RunnerFactory.allFrom is being called"); + + List runners = new ArrayList<>(); + + List frameworks = TestFramework.getListOfFrameworks(project); + System.out.println("Number of frameworks detected: " + frameworks.size()); + + TestFramework.getListOfFrameworks(project).forEach(framework -> { + System.out.println("Detected framework: " + framework.toString()); + + Runner runner = create(framework, new ProjectClassLoader(project).classpath(), + surefireEnvironment(project), project.getBasedir().toPath()); + + if (runner != null) { + runners.add(runner); + System.out.println("Created runner for framework: " + framework.toString()); + } else { + System.out.println("Failed to create runner for framework: " + framework.toString()); + } + }); + + return runners; + } private static Stream emptyIfNull(final T t) { @@ -72,3 +93,4 @@ public static Map surefireEnvironment(final MavenProject project .collect(Collectors.toMap(Xpp3Dom::getName, v -> v.getValue() == null ? "" : v.getValue())); } } + From a7c1459aea7911e6a0ec0ac92aee85241d2b0f46 Mon Sep 17 00:00:00 2001 From: srafi Date: Thu, 3 Oct 2024 10:57:02 -0400 Subject: [PATCH 2/2] New version for IT tests --- pom.xml | 2 +- testrunner-core-plugin/pom.xml | 2 +- testrunner-gradle-plugin/pom.xml | 2 +- testrunner-maven-plugin/pom.xml | 2 +- testrunner-running/pom.xml | 2 +- testrunner-testing/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index ce074b0..a5852c0 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ edu.illinois.cs testrunner - 1.3-SNAPSHOT + 1.4-SNAPSHOT testrunner-core-plugin diff --git a/testrunner-core-plugin/pom.xml b/testrunner-core-plugin/pom.xml index 9a2bfeb..3d51987 100644 --- a/testrunner-core-plugin/pom.xml +++ b/testrunner-core-plugin/pom.xml @@ -7,7 +7,7 @@ edu.illinois.cs testrunner - 1.3-SNAPSHOT + 1.4-SNAPSHOT testrunner-core-plugin diff --git a/testrunner-gradle-plugin/pom.xml b/testrunner-gradle-plugin/pom.xml index aa25845..11ae7b5 100644 --- a/testrunner-gradle-plugin/pom.xml +++ b/testrunner-gradle-plugin/pom.xml @@ -7,7 +7,7 @@ edu.illinois.cs testrunner - 1.3-SNAPSHOT + 1.4-SNAPSHOT testrunner-gradle-plugin diff --git a/testrunner-maven-plugin/pom.xml b/testrunner-maven-plugin/pom.xml index 244be02..384e4bf 100644 --- a/testrunner-maven-plugin/pom.xml +++ b/testrunner-maven-plugin/pom.xml @@ -7,7 +7,7 @@ edu.illinois.cs testrunner - 1.3-SNAPSHOT + 1.4-SNAPSHOT testrunner-maven-plugin diff --git a/testrunner-running/pom.xml b/testrunner-running/pom.xml index 3c92d87..193dad5 100644 --- a/testrunner-running/pom.xml +++ b/testrunner-running/pom.xml @@ -7,7 +7,7 @@ edu.illinois.cs testrunner - 1.3-SNAPSHOT + 1.4-SNAPSHOT testrunner-running diff --git a/testrunner-testing/pom.xml b/testrunner-testing/pom.xml index 139b931..85b39a2 100644 --- a/testrunner-testing/pom.xml +++ b/testrunner-testing/pom.xml @@ -7,7 +7,7 @@ edu.illinois.cs testrunner - 1.3-SNAPSHOT + 1.4-SNAPSHOT testrunner-testing