diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/FileSpec.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/FileSpec.kt index 60252367..3dabc39e 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/FileSpec.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/FileSpec.kt @@ -3,7 +3,9 @@ package com.beust.kobalt import com.beust.kobalt.misc.log import java.io.File import java.nio.file.* +import java.nio.file.FileVisitResult.CONTINUE import java.nio.file.attribute.BasicFileAttributes +import java.util.* sealed class IFileSpec { abstract fun toFiles(directory: String): List @@ -14,25 +16,57 @@ sealed class IFileSpec { override public fun toString() = spec } - class GlobSpec(val spec: String) : IFileSpec() { - override public fun toFiles(directory: String): List { - val result = arrayListOf() + class GlobSpec(val includeSpec: ArrayList, val excludeSpec: ArrayList) : IFileSpec() { + + constructor(spec: String) : this(arrayListOf(spec), arrayListOf()) + + override public fun toFiles(directory: String): List { + + val result = arrayListOf() + val includeMatchers = prepareMatchers(includeSpec.toTypedArray()) + val excludeMatchers = prepareMatchers(excludeSpec.toTypedArray()) - val matcher = FileSystems.getDefault().getPathMatcher("glob:$spec") Files.walkFileTree(Paths.get(directory), object : SimpleFileVisitor() { override public fun visitFile(path: Path, attrs: BasicFileAttributes): FileVisitResult { + val rel = Paths.get(directory).relativize(path) - if (matcher.matches(rel)) { - log(3, "Adding ${rel.toFile()}") - result.add(rel.toFile()) + excludeMatchers.forEach { + if (it.matches(rel)) { + log(3, "Removing ${rel.toFile()}") + return CONTINUE + } } - return FileVisitResult.CONTINUE + includeMatchers.forEach { + if (it.matches(rel)) { + log(3, "Adding ${rel.toFile()}") + result.add(rel.toFile()) + return CONTINUE + } + } + + return CONTINUE } }) return result } - override public fun toString() = spec + override public fun toString(): String { + var result = "" + includeSpec.apply { + if (!isEmpty()) { + result += "Included files: " + joinToString { ", " } + } + } + excludeSpec.apply { + if (!isEmpty()) { + result += "Excluded files: " + joinToString { ", " } + } + } + return result + } + + private fun prepareMatchers(specs: Array): List = + specs.map { it -> FileSystems.getDefault().getPathMatcher("glob:$it") } } -} +} \ No newline at end of file diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/TestDirective.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/TestDirective.kt index f62a9098..ac7eed22 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/TestDirective.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/TestDirective.kt @@ -11,9 +11,21 @@ class TestConfig(val project: Project) { fun jvmArgs(vararg arg: String) { project.testJvmArgs.addAll(arg) } + + fun includes(vararg arg: String) { + project.testIncludes.apply { + clear() + addAll(arg) + } + } + + fun excludes(vararg arg: String) { + project.testExcludes.apply { + clear() + addAll(arg) + } + } } @Directive fun Project.test(init: TestConfig.() -> Unit) = TestConfig(this).apply { init() } - - diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/Project.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/Project.kt index b5de85d1..0e1f3e8c 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/Project.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/Project.kt @@ -29,6 +29,8 @@ open class Project( val testJvmArgs = arrayListOf() val projectProperties = ProjectProperties() + val testIncludes = arrayListOf("**/*Test.class") + val testExcludes = arrayListOf() override fun equals(other: Any?): Boolean { return name == (other as Project).name diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/GenericRunner.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/GenericRunner.kt index 0cf3eb89..21a0a89b 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/GenericRunner.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/GenericRunner.kt @@ -1,5 +1,6 @@ package com.beust.kobalt.internal +import com.beust.kobalt.IFileSpec import com.beust.kobalt.JavaInfo import com.beust.kobalt.SystemProperties import com.beust.kobalt.TaskResult @@ -7,7 +8,7 @@ import com.beust.kobalt.api.* import com.beust.kobalt.misc.KFiles import com.beust.kobalt.misc.log import java.io.File -import java.net.URLClassLoader +import java.util.* /** * Base class for testing frameworks that are invoked from a main class with arguments. Test runners can @@ -25,33 +26,21 @@ abstract class GenericTestRunner : ITestRunnerContributor { if (project.testDependencies.any { it.id.contains(dependencyName)}) IAffinity.DEFAULT_POSITIVE_AFFINITY else 0 - protected fun findTestClasses(project: Project, classpath: List, - classFilter : (Class<*>) -> Boolean = {true}): List { + protected fun findTestClasses(project: Project): List { val path = KFiles.joinDir(project.directory, project.buildDirectory, KFiles.TEST_CLASSES_DIR) - val result = KFiles.findRecursively(File(path), arrayListOf(File(".")), { - file -> file.endsWith(".class") - }).map { - it.replace("/", ".").replace("\\", ".").replace(".class", "").substring(2) - }.filter { - try { - // Only keep classes with a parameterless constructor - val urls = arrayOf(File(path).toURI().toURL()) + - classpath.map { it.jarFile.get().toURI().toURL() } - val cl = URLClassLoader(urls).loadClass(it) - val constructor = cl.getConstructor() - // If we get past this, we have a default constructor - classFilter(cl) - } catch(ex: Exception) { - log(2, "Skipping non test class $it: ${ex.message}") - false - } - } + val result = IFileSpec.GlobSpec(toClassPaths(project.testIncludes), toClassPaths(project.testExcludes)) + .toFiles(path).map { + it.toString().replace("/", ".").replace("\\", ".").replace(".class", "") + } log(2, "Found ${result.size} test classes") return result } + private fun toClassPaths(paths: List): ArrayList = + paths.map { if (it.endsWith(".class")) it else it + ".class" }.toArrayList() + /** * @return true if all the tests passed */ diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JUnitRunner.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JUnitRunner.kt index bf142419..940cf8e0 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JUnitRunner.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JUnitRunner.kt @@ -9,15 +9,6 @@ open public class JUnitRunner() : GenericTestRunner() { override val dependencyName = "junit" - override fun args(project: Project, classpath: List) - = findTestClasses(project, classpath) { - // Only return a class if it contains at least one @Test method, otherwise - // JUnit 4 throws an exception :-( - it.declaredMethods.flatMap { - it.annotations.toList() - }.filter { - ann: Annotation -> ann.javaClass.name.contains("Test") - }.size > 0 - } + override fun args(project: Project, classpath: List) = findTestClasses(project) } diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt index a960fabe..14c8fee1 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt @@ -15,18 +15,18 @@ public class TestNgRunner() : GenericTestRunner() { override fun args(project: Project, classpath: List) = arrayListOf().apply { if (project.testArgs.size > 0) { addAll(project.testArgs) + } + + val testngXml = File(project.directory, KFiles.joinDir("src", "test", "resources", "testng.xml")) + if (testngXml.exists()) { + add(testngXml.absolutePath) } else { - val testngXml = File(project.directory, KFiles.joinDir("src", "test", "resources", "testng.xml")) - if (testngXml.exists()) { - add(testngXml.absolutePath) + val testClasses = findTestClasses(project) + if (testClasses.size > 0) { + add("-testclass") + add(testClasses.joinToString(",")) } else { - val testClasses = findTestClasses(project, classpath) - if (testClasses.size > 0) { - add("-testclass") - add(testClasses.joinToString(",")) - } else { - warn("Couldn't find any test classes for ${project.name}") - } + warn("Couldn't find any test classes for ${project.name}") } } } diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/misc/JarUtils.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/misc/JarUtils.kt index 68fd7a11..96bdda6a 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/misc/JarUtils.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/misc/JarUtils.kt @@ -28,10 +28,9 @@ public class JarUtils { } } - private val DEFAULT_JAR_EXCLUDES = arrayListOf( - IFileSpec.GlobSpec("META-INF/*.SF"), - IFileSpec.GlobSpec("META-INF/*.DSA"), - IFileSpec.GlobSpec("META-INF/*.RSA")) + private val DEFAULT_JAR_EXCLUDES = + IFileSpec.GlobSpec(arrayListOf(), + arrayListOf("META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA")) public fun addSingleFile(directory: String, file: IncludedFile, outputStream: ZipOutputStream, expandJarFiles: Boolean, onError: (Exception) -> Unit = DEFAULT_HANDLER) { @@ -153,7 +152,7 @@ class IncludedFile(val fromOriginal: From, val toOriginal: To, val specs: List) = isExcluded(file.path, excludes) - - fun isExcluded(file: String, excludes: List) : Boolean { - if (excludes.isEmpty()) { - return false - } else { - val ex = excludes.map { - FileSystems.getDefault().getPathMatcher("glob:${it.spec}") - } - ex.forEach { - if (it.matches(Paths.get(file))) { - log(3, "Excluding $file") - return true - } - } - } - return false - } + fun isExcluded(file: File, excludes: IFileSpec.GlobSpec) = isExcluded(file.path, excludes) + fun isExcluded(file: String, excludes: IFileSpec.GlobSpec): Boolean = + excludes.toFiles(file).isEmpty() } fun findRecursively(directory: File, function: Function1): List { diff --git a/src/main/kotlin/com/beust/kobalt/plugin/packaging/PackagingPlugin.kt b/src/main/kotlin/com/beust/kobalt/plugin/packaging/PackagingPlugin.kt index 77779c9a..db1c7e30 100644 --- a/src/main/kotlin/com/beust/kobalt/plugin/packaging/PackagingPlugin.kt +++ b/src/main/kotlin/com/beust/kobalt/plugin/packaging/PackagingPlugin.kt @@ -43,7 +43,7 @@ class PackagingPlugin @Inject constructor(val dependencyManager : DependencyMana const val TASK_INSTALL: String = "install" - fun findIncludedFiles(directory: String, files: List, excludes: List) + fun findIncludedFiles(directory: String, files: List, excludes: IFileSpec.GlobSpec) : List { val result = arrayListOf() files.forEach { includedFile -> @@ -313,7 +313,7 @@ class PackageConfig(val project: Project) : AttributeHolder { open class Zip(open var name: String? = null) { // internal val includes = arrayListOf() - internal val excludes = arrayListOf() + internal val excludes = GlobSpec(arrayListOf(), arrayListOf()) @Directive public fun from(s: String) = From(s) @@ -323,12 +323,12 @@ open class Zip(open var name: String? = null) { @Directive public fun exclude(vararg files: String) { - files.forEach { excludes.add(GlobSpec(it)) } + files.forEach { excludes.excludeSpec.add(it) } } @Directive public fun exclude(vararg specs: GlobSpec) { - specs.forEach { excludes.add(it) } + specs.forEach { excludes.excludeSpec.addAll(it.excludeSpec) } } @Directive