diff options
13 files changed, 271 insertions, 209 deletions
diff --git a/integration/test/resources/upickle/build.sc b/integration/test/resources/upickle/build.sc index e1383771..2ea7668d 100644 --- a/integration/test/resources/upickle/build.sc +++ b/integration/test/resources/upickle/build.sc @@ -90,7 +90,7 @@ trait UpickleModule extends CrossSbtModule with PublishModule{ def platformSegment: String } -trait UpickleTestModule extends TestModule{ +trait UpickleTestModule extends TestModule with ScalaModule{ def platformSegment: String def ivyDeps = Agg( diff --git a/scalajslib/src/mill/scalajslib/ScalaJSModule.scala b/scalajslib/src/mill/scalajslib/ScalaJSModule.scala index dbf693ff..1596cdb6 100644 --- a/scalajslib/src/mill/scalajslib/ScalaJSModule.scala +++ b/scalajslib/src/mill/scalajslib/ScalaJSModule.scala @@ -190,9 +190,7 @@ trait TestScalaJSModule extends ScalaJSModule with TestModule { fastOptTest().path.toIO ) - val (doneMsg, results) = scalaWorker - .worker() - .runTests( + val (doneMsg, results) = Lib.runTests( _ => Seq(framework), runClasspath().map(_.path), Agg(compile().classes.path), diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala index b8ae0fd4..ea56b22e 100644 --- a/scalalib/src/mill/scalalib/JavaModule.scala +++ b/scalalib/src/mill/scalalib/JavaModule.scala @@ -17,7 +17,11 @@ import mill.util.Loose.Agg * Core configuration required to compile a single Scala compilation target */ trait JavaModule extends mill.Module with TaskModule { outer => - + trait Tests extends TestModule{ + override def moduleDeps = Seq(outer) + override def repositories = outer.repositories + override def javacOptions = outer.javacOptions + } def defaultCommandName() = "run" def resolvePublishDependency: Task[Dep => publish.Dependency] = T.task{ @@ -281,4 +285,67 @@ trait JavaModule extends mill.Module with TaskModule { outer => def artifactName: T[String] = millModuleSegments.parts.mkString("-") def artifactId: T[String] = artifactName() -}
\ No newline at end of file +} + +trait TestModule extends JavaModule with TaskModule { + override def defaultCommandName() = "test" + def testFrameworks: T[Seq[String]] + + def forkWorkingDir = ammonite.ops.pwd + + def test(args: String*) = T.command{ + val outputPath = T.ctx().dest/"out.json" + + Jvm.subprocess( + mainClass = "mill.scalaworker.ScalaWorker", + classPath = ScalaWorkerModule.classpath(), + jvmArgs = forkArgs(), + envArgs = forkEnv(), + mainArgs = + Seq(testFrameworks().length.toString) ++ + testFrameworks() ++ + Seq(runClasspath().length.toString) ++ + runClasspath().map(_.path.toString) ++ + Seq(args.length.toString) ++ + args ++ + Seq(outputPath.toString, T.ctx().log.colored.toString, compile().classes.path.toString, T.ctx().home.toString), + workingDir = forkWorkingDir + ) + + val jsonOutput = ujson.read(outputPath.toIO) + val (doneMsg, results) = upickle.default.readJs[(String, Seq[TestRunner.Result])](jsonOutput) + TestModule.handleResults(doneMsg, results) + + } + def testLocal(args: String*) = T.command{ + val outputPath = T.ctx().dest/"out.json" + + Lib.runTests( + TestRunner.frameworks(testFrameworks()), + runClasspath().map(_.path), + Agg(compile().classes.path), + args + ) + + val jsonOutput = ujson.read(outputPath.toIO) + val (doneMsg, results) = upickle.default.readJs[(String, Seq[TestRunner.Result])](jsonOutput) + TestModule.handleResults(doneMsg, results) + + } +} + +object TestModule{ + def handleResults(doneMsg: String, results: Seq[TestRunner.Result]) = { + + val badTests = results.filter(x => Set("Error", "Failure").contains(x.status)) + if (badTests.isEmpty) Result.Success((doneMsg, results)) + else { + val suffix = if (badTests.length == 1) "" else " and " + (badTests.length-1) + " more" + + Result.Failure( + badTests.head.fullyQualifiedName + " " + badTests.head.selector + suffix, + Some((doneMsg, results)) + ) + } + } +} diff --git a/scalalib/src/mill/scalalib/Lib.scala b/scalalib/src/mill/scalalib/Lib.scala index 7b4b5bdb..3eb2defd 100644 --- a/scalalib/src/mill/scalalib/Lib.scala +++ b/scalalib/src/mill/scalalib/Lib.scala @@ -1,14 +1,22 @@ package mill package scalalib -import java.io.File +import java.io.{File, FileInputStream} +import java.lang.annotation.Annotation +import java.util.zip.ZipInputStream import javax.tools.ToolProvider import ammonite.ops._ import ammonite.util.Util import coursier.{Cache, Dependency, Fetch, Repository, Resolution} +import mill.Agg import mill.eval.{PathRef, Result} -import mill.util.Loose.Agg +import mill.modules.Jvm + +import mill.util.Ctx +import sbt.testing._ + +import scala.collection.mutable object CompilationResult { implicit val jsonFormatter: upickle.default.ReadWriter[CompilationResult] = upickle.default.macroRW @@ -194,4 +202,109 @@ object Lib{ force = false ) + def runTests(frameworkInstances: ClassLoader => Seq[sbt.testing.Framework], + entireClasspath: Agg[Path], + testClassfilePath: Agg[Path], + args: Seq[String]) + (implicit ctx: Ctx.Log with Ctx.Home): (String, Seq[mill.scalalib.TestRunner.Result]) = { + Jvm.inprocess(entireClasspath, classLoaderOverrideSbtTesting = true, cl => { + val frameworks = frameworkInstances(cl) + + val events = mutable.Buffer.empty[Event] + + val doneMessages = frameworks.map { framework => + val runner = framework.runner(args.toArray, args.toArray, cl) + + val testClasses = discoverTests(cl, framework, testClassfilePath) + + val tasks = runner.tasks( + for ((cls, fingerprint) <- testClasses.toArray) + yield new TaskDef(cls.getName.stripSuffix("$"), fingerprint, true, Array(new SuiteSelector)) + ) + + for (t <- tasks) { + t.execute( + new EventHandler { + def handle(event: Event) = events.append(event) + }, + Array( + new Logger { + def debug(msg: String) = ctx.log.outputStream.println(msg) + + def error(msg: String) = ctx.log.outputStream.println(msg) + + def ansiCodesSupported() = true + + def warn(msg: String) = ctx.log.outputStream.println(msg) + + def trace(t: Throwable) = t.printStackTrace(ctx.log.outputStream) + + def info(msg: String) = ctx.log.outputStream.println(msg) + }) + ) + } + ctx.log.outputStream.println(runner.done()) + } + + val results = for(e <- events) yield { + val ex = if (e.throwable().isDefined) Some(e.throwable().get) else None + mill.scalalib.TestRunner.Result( + e.fullyQualifiedName(), + e.selector() match{ + case s: NestedSuiteSelector => s.suiteId() + case s: NestedTestSelector => s.suiteId() + "." + s.testName() + case s: SuiteSelector => s.toString + case s: TestSelector => s.testName() + case s: TestWildcardSelector => s.testWildcard() + }, + e.duration(), + e.status().toString, + ex.map(_.getClass.getName), + ex.map(_.getMessage), + ex.map(_.getStackTrace) + ) + } + + (doneMessages.mkString("\n"), results) + }) + } + + def listClassFiles(base: Path): Iterator[String] = { + if (base.isDir) ls.rec(base).toIterator.filter(_.ext == "class").map(_.relativeTo(base).toString) + else { + val zip = new ZipInputStream(new FileInputStream(base.toIO)) + Iterator.continually(zip.getNextEntry).takeWhile(_ != null).map(_.getName).filter(_.endsWith(".class")) + } + } + + def discoverTests(cl: ClassLoader, framework: Framework, classpath: Agg[Path]) = { + + val fingerprints = framework.fingerprints() + + val testClasses = classpath.flatMap { base => + // Don't blow up if there are no classfiles representing + // the tests to run Instead just don't run anything + if (!exists(base)) Nil + else listClassFiles(base).flatMap { path => + val cls = cl.loadClass(path.stripSuffix(".class").replace('/', '.')) + fingerprints.find { + case f: SubclassFingerprint => + !cls.isInterface && + (f.isModule == cls.getName.endsWith("$")) && + cl.loadClass(f.superclassName()).isAssignableFrom(cls) + case f: AnnotatedFingerprint => + val annotationCls = cl.loadClass(f.annotationName()).asInstanceOf[Class[Annotation]] + (f.isModule == cls.getName.endsWith("$")) && + ( + cls.isAnnotationPresent(annotationCls) || + cls.getDeclaredMethods.exists(_.isAnnotationPresent(annotationCls)) + ) + + }.map { f => (cls, f) } + } + } + + testClasses + } + } diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala index b98f248e..a2ca09c5 100644 --- a/scalalib/src/mill/scalalib/ScalaModule.scala +++ b/scalalib/src/mill/scalalib/ScalaModule.scala @@ -18,7 +18,7 @@ import mill.util.DummyInputStream trait ScalaModule extends JavaModule { outer => def scalaWorker: ScalaWorkerModule = mill.scalalib.ScalaWorkerModule - trait Tests extends TestModule{ + trait Tests extends TestModule with ScalaModule{ def scalaVersion = outer.scalaVersion() override def repositories = outer.repositories override def scalacPluginIvyDeps = outer.scalacPluginIvyDeps @@ -195,64 +195,3 @@ trait ScalaModule extends JavaModule { outer => } -object TestModule{ - def handleResults(doneMsg: String, results: Seq[TestRunner.Result]) = { - - val badTests = results.filter(x => Set("Error", "Failure").contains(x.status)) - if (badTests.isEmpty) Result.Success((doneMsg, results)) - else { - val suffix = if (badTests.length == 1) "" else " and " + (badTests.length-1) + " more" - - Result.Failure( - badTests.head.fullyQualifiedName + " " + badTests.head.selector + suffix, - Some((doneMsg, results)) - ) - } - } -} -trait TestModule extends ScalaModule with TaskModule { - override def defaultCommandName() = "test" - def testFrameworks: T[Seq[String]] - - def forkWorkingDir = ammonite.ops.pwd - - def test(args: String*) = T.command{ - val outputPath = T.ctx().dest/"out.json" - - Jvm.subprocess( - mainClass = "mill.scalaworker.ScalaWorker", - classPath = scalaWorker.classpath(), - jvmArgs = forkArgs(), - envArgs = forkEnv(), - mainArgs = - Seq(testFrameworks().length.toString) ++ - testFrameworks() ++ - Seq(runClasspath().length.toString) ++ - runClasspath().map(_.path.toString) ++ - Seq(args.length.toString) ++ - args ++ - Seq(outputPath.toString, T.ctx().log.colored.toString, compile().classes.path.toString, T.ctx().home.toString), - workingDir = forkWorkingDir - ) - - val jsonOutput = ujson.read(outputPath.toIO) - val (doneMsg, results) = upickle.default.readJs[(String, Seq[TestRunner.Result])](jsonOutput) - TestModule.handleResults(doneMsg, results) - - } - def testLocal(args: String*) = T.command{ - val outputPath = T.ctx().dest/"out.json" - - scalaWorker.worker().runTests( - TestRunner.frameworks(testFrameworks()), - runClasspath().map(_.path), - Agg(compile().classes.path), - args - ) - - val jsonOutput = ujson.read(outputPath.toIO) - val (doneMsg, results) = upickle.default.readJs[(String, Seq[TestRunner.Result])](jsonOutput) - TestModule.handleResults(doneMsg, results) - - } -} diff --git a/scalalib/src/mill/scalalib/ScalaWorkerApi.scala b/scalalib/src/mill/scalalib/ScalaWorkerApi.scala index f6500ae8..0b6a931b 100644 --- a/scalalib/src/mill/scalalib/ScalaWorkerApi.scala +++ b/scalalib/src/mill/scalalib/ScalaWorkerApi.scala @@ -5,7 +5,6 @@ import ammonite.ops.Path import coursier.Cache import coursier.maven.MavenRepository import mill.Agg -import mill.scalalib.TestRunner.Result import mill.T import mill.define.{Discover, Worker} import mill.scalalib.Lib.resolveDependencies @@ -68,11 +67,6 @@ trait ScalaWorkerApi { upstreamCompileOutput: Seq[CompilationResult]) (implicit ctx: mill.util.Ctx): mill.eval.Result[CompilationResult] - def runTests(frameworkInstances: ClassLoader => Seq[sbt.testing.Framework], - entireClasspath: Agg[Path], - testClassfilePath: Agg[Path], - args: Seq[String]) - (implicit ctx: mill.util.Ctx.Log with mill.util.Ctx.Home): (String, Seq[Result]) def discoverMainClasses(compilationResult: CompilationResult) (implicit ctx: mill.util.Ctx): Seq[String] diff --git a/scalalib/test/resources/hello-java/app/src/hello/Main.java b/scalalib/test/resources/hello-java/app/src/hello/Main.java new file mode 100644 index 00000000..23ddd679 --- /dev/null +++ b/scalalib/test/resources/hello-java/app/src/hello/Main.java @@ -0,0 +1,10 @@ +package hello; + +public class Main{ + public static String getMessage(String[] args){ + return Core.msg() + " " + args[0]; + } + public static void main(String[] args){ + System.out.println(getMessage(args)); + } +}
\ No newline at end of file diff --git a/scalalib/test/resources/hello-java/app/test/src/hello/MyAppTests.java b/scalalib/test/resources/hello-java/app/test/src/hello/MyAppTests.java new file mode 100644 index 00000000..df0d0351 --- /dev/null +++ b/scalalib/test/resources/hello-java/app/test/src/hello/MyAppTests.java @@ -0,0 +1,18 @@ +package hello; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class MyAppTests { + + @Test + public void coreTest() { + assertEquals(Core.msg(), "Hello World"); + } + + @Test + public void appTest() { + assertEquals(Main.getMessage(new String[]{"lols"}), "Hello World lols"); + } + +}
\ No newline at end of file diff --git a/scalalib/test/resources/hello-java/core/src/hello/Core.java b/scalalib/test/resources/hello-java/core/src/hello/Core.java index bef1a5a0..3ecb1f61 100644 --- a/scalalib/test/resources/hello-java/core/src/hello/Core.java +++ b/scalalib/test/resources/hello-java/core/src/hello/Core.java @@ -4,5 +4,4 @@ public class Core{ public static String msg(){ return "Hello World"; } - }
\ No newline at end of file diff --git a/scalalib/test/resources/hello-java/core/test/src/hello/MyCoreTests.java b/scalalib/test/resources/hello-java/core/test/src/hello/MyCoreTests.java new file mode 100644 index 00000000..38bebaeb --- /dev/null +++ b/scalalib/test/resources/hello-java/core/test/src/hello/MyCoreTests.java @@ -0,0 +1,15 @@ +package hello; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class MyCoreTests { + @Test + public void msgTest() { + assertEquals(Core.msg(), "Hello World!!"); + } + @Test + public void lengthTest() { + assertEquals(Core.msg().length(), 11); + } +}
\ No newline at end of file diff --git a/scalalib/test/resources/hello-java/main/src/hello/Main.java b/scalalib/test/resources/hello-java/main/src/hello/Main.java deleted file mode 100644 index 44b927bd..00000000 --- a/scalalib/test/resources/hello-java/main/src/hello/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package hello; - -public class Main{ - public static void main(String[] args){ - System.out.println(Core.msg() + " " + args[0]); - } -}
\ No newline at end of file diff --git a/scalalib/test/src/mill/scalalib/HelloJavaTests.scala b/scalalib/test/src/mill/scalalib/HelloJavaTests.scala index 7794d5a5..bcfdd19f 100644 --- a/scalalib/test/src/mill/scalalib/HelloJavaTests.scala +++ b/scalalib/test/src/mill/scalalib/HelloJavaTests.scala @@ -1,8 +1,10 @@ -package mill.scalalib +package mill +package scalalib import ammonite.ops.{%, %%, cp, ls, mkdir, pwd, rm, up} import ammonite.ops.ImplicitWd._ +import mill.eval.Result import mill.util.{TestEvaluator, TestUtil} import utest._ import utest.framework.TestPath @@ -12,9 +14,17 @@ object HelloJavaTests extends TestSuite { object HelloJava extends TestUtil.BaseModule{ def millSourcePath = TestUtil.getSrcPathBase() / millOuterCtx.enclosing.split('.') - object core extends JavaModule - object main extends JavaModule{ + trait JUnitTests extends TestModule{ + def testFrameworks = Seq("com.novocode.junit.JUnitFramework") + def ivyDeps = Agg(ivy"com.novocode:junit-interface:0.11") + } + + object core extends JavaModule{ + object test extends Tests with JUnitTests + } + object app extends JavaModule{ def moduleDeps = Seq(core) + object test extends Tests with JUnitTests } } val resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-java" @@ -28,12 +38,12 @@ object HelloJavaTests extends TestSuite { eval } def tests: Tests = Tests { - 'scalaVersion - { + 'compile - { val eval = init() val Right((res1, n1)) = eval.apply(HelloJava.core.compile) val Right((res2, 0)) = eval.apply(HelloJava.core.compile) - val Right((res3, n2)) = eval.apply(HelloJava.main.compile) + val Right((res3, n2)) = eval.apply(HelloJava.app.compile) assert( res1 == res2, @@ -49,12 +59,33 @@ object HelloJavaTests extends TestSuite { val eval = init() val Right((ref1, _)) = eval.apply(HelloJava.core.docJar) - val Right((ref2, _)) = eval.apply(HelloJava.main.docJar) + val Right((ref2, _)) = eval.apply(HelloJava.app.docJar) assert( %%("jar", "tf", ref1.path).out.lines.contains("hello/Core.html"), %%("jar", "tf", ref2.path).out.lines.contains("hello/Main.html") ) } + 'test - { + val eval = init() + + val Left(Result.Failure(ref1, Some(v1))) = eval.apply(HelloJava.core.test.test()) + + assert( + v1._2(0).fullyQualifiedName == "hello.MyCoreTests.lengthTest", + v1._2(0).status == "Success", + v1._2(1).fullyQualifiedName == "hello.MyCoreTests.msgTest", + v1._2(1).status == "Failure" + ) + + val Right((v2, _)) = eval.apply(HelloJava.app.test.test()) + + assert( + v2._2(0).fullyQualifiedName == "hello.MyAppTests.appTest", + v2._2(0).status == "Success", + v2._2(1).fullyQualifiedName == "hello.MyAppTests.coreTest", + v2._2(1).status == "Success" + ) + } } } diff --git a/scalaworker/src/mill/scalaworker/ScalaWorker.scala b/scalaworker/src/mill/scalaworker/ScalaWorker.scala index 9a4c317c..08a31fc6 100644 --- a/scalaworker/src/mill/scalaworker/ScalaWorker.scala +++ b/scalaworker/src/mill/scalaworker/ScalaWorker.scala @@ -1,34 +1,19 @@ package mill.scalaworker -import java.io.{File, FileInputStream} -import java.lang.annotation.Annotation -import java.net.URLClassLoader +import java.io.File import java.util.Optional -import java.util.zip.ZipInputStream -import javax.tools.ToolProvider -import ammonite.ops.{Path, exists, ls, mkdir, rm, up} +import ammonite.ops.{Path, exists, ls, mkdir} import ammonite.util.Colors import mill.Agg -import mill.define.Worker import mill.eval.PathRef -import mill.modules.Jvm import mill.scalalib.{CompilationResult, Lib, TestRunner} import xsbti.compile.{CompilerCache => _, FileAnalysisStore => _, ScalaInstance => _, _} import mill.scalalib.Lib.grepJar -import mill.scalalib.TestRunner.Result import mill.util.{Ctx, PrintLogger} import sbt.internal.inc._ -import sbt.internal.inc.classfile.Analyze -import sbt.internal.inc.classpath.ClasspathUtilities -import sbt.internal.inc.javac.JavaCompiler import sbt.internal.util.{ConsoleOut, MainAppender} -import sbt.io.PathFinder -import sbt.testing._ -import sbt.util.{InterfaceUtil, LogExchange} -import xsbti.AnalysisCallback - -import scala.collection.mutable +import sbt.util.LogExchange case class MockedLookup(am: File => Optional[CompileAnalysis]) extends PerClasspathEntryLookup { override def analysis(classpathEntry: File): Optional[CompileAnalysis] = @@ -68,7 +53,7 @@ object ScalaWorker{ ) val home = Path(homeStr) } - val result = new ScalaWorker(null, null).runTests( + val result = Lib.runTests( frameworkInstances = TestRunner.frameworks(frameworks), entireClasspath = Agg.from(classpath.map(Path(_))), testClassfilePath = Agg(Path(testCp)), @@ -262,104 +247,4 @@ class ScalaWorker(ctx0: mill.util.Ctx, mill.eval.Result.Success(CompilationResult(zincFile, PathRef(classesDir))) }catch{case e: CompileFailed => mill.eval.Result.Failure(e.toString)} } - - def runTests(frameworkInstances: ClassLoader => Seq[sbt.testing.Framework], - entireClasspath: Agg[Path], - testClassfilePath: Agg[Path], - args: Seq[String]) - (implicit ctx: Ctx.Log with Ctx.Home): (String, Seq[Result]) = { - Jvm.inprocess(entireClasspath, classLoaderOverrideSbtTesting = true, cl => { - val frameworks = frameworkInstances(cl) - - val events = mutable.Buffer.empty[Event] - - val doneMessages = frameworks.map { framework => - val runner = framework.runner(args.toArray, args.toArray, cl) - - val testClasses = discoverTests(cl, framework, testClassfilePath) - - val tasks = runner.tasks( - for ((cls, fingerprint) <- testClasses.toArray) - yield new TaskDef(cls.getName.stripSuffix("$"), fingerprint, true, Array(new SuiteSelector)) - ) - - for (t <- tasks) { - t.execute( - new EventHandler { - def handle(event: Event) = events.append(event) - }, - Array( - new Logger { - def debug(msg: String) = ctx.log.outputStream.println(msg) - - def error(msg: String) = ctx.log.outputStream.println(msg) - - def ansiCodesSupported() = true - - def warn(msg: String) = ctx.log.outputStream.println(msg) - - def trace(t: Throwable) = t.printStackTrace(ctx.log.outputStream) - - def info(msg: String) = ctx.log.outputStream.println(msg) - }) - ) - } - ctx.log.outputStream.println(runner.done()) - } - - val results = for(e <- events) yield { - val ex = if (e.throwable().isDefined) Some(e.throwable().get) else None - Result( - e.fullyQualifiedName(), - e.selector() match{ - case s: NestedSuiteSelector => s.suiteId() - case s: NestedTestSelector => s.suiteId() + "." + s.testName() - case s: SuiteSelector => s.toString - case s: TestSelector => s.testName() - case s: TestWildcardSelector => s.testWildcard() - }, - e.duration(), - e.status().toString, - ex.map(_.getClass.getName), - ex.map(_.getMessage), - ex.map(_.getStackTrace) - ) - } - - (doneMessages.mkString("\n"), results) - }) - - } - def listClassFiles(base: Path): Iterator[String] = { - if (base.isDir) ls.rec(base).toIterator.filter(_.ext == "class").map(_.relativeTo(base).toString) - else { - val zip = new ZipInputStream(new FileInputStream(base.toIO)) - Iterator.continually(zip.getNextEntry).takeWhile(_ != null).map(_.getName).filter(_.endsWith(".class")) - } - } - def discoverTests(cl: ClassLoader, framework: Framework, classpath: Agg[Path]) = { - - - val fingerprints = framework.fingerprints() - val testClasses = classpath.flatMap { base => - // Don't blow up if there are no classfiles representing - // the tests to run Instead just don't run anything - if (!exists(base)) Nil - else listClassFiles(base).flatMap { path => - val cls = cl.loadClass(path.stripSuffix(".class").replace('/', '.')) - fingerprints.find { - case f: SubclassFingerprint => - !cls.isInterface && - (f.isModule == cls.getName.endsWith("$")) && - cl.loadClass(f.superclassName()).isAssignableFrom(cls) - case f: AnnotatedFingerprint => - (f.isModule == cls.getName.endsWith("$")) && - cls.isAnnotationPresent( - cl.loadClass(f.annotationName()).asInstanceOf[Class[Annotation]] - ) - }.map { f => (cls, f) } - } - } - testClasses - } } |