restructure tests into groups, add test-case descriptions
diff --git a/tests/integrationtests/jvm/JvmAnnotationProcessingIntegrationTests.kt b/tests/integrationtests/jvm/JvmAnnotationProcessingIntegrationTests.kt
index dfee40f..9a26987 100644
--- a/tests/integrationtests/jvm/JvmAnnotationProcessingIntegrationTests.kt
+++ b/tests/integrationtests/jvm/JvmAnnotationProcessingIntegrationTests.kt
@@ -20,70 +20,67 @@
 
 class JvmAnnotationProcessingIntegrationTests : AssertionTestCase("tests/integrationtests/jvm/kapt") {
     @Test
-    fun kotlinOnly() = withTestCaseJar("ap_kotlin.jar") {
-        assertContainsEntries("tests/smoke/kapt/kotlin/AutoValue_TestKtValue.class")
+    fun testKotlinOnlyAnnotationProcessing() {
+        jarTestCase("ap_kotlin.jar", description = "annotation processing should work") {
+            assertContainsEntries("tests/smoke/kapt/kotlin/AutoValue_TestKtValue.class")
+        }
+        jarTestCase("ap_kotlin_mixed_no_plugin.jar", description = "annotation processing should not kick in for deps.") {
+            assertDoesNotContainEntries(
+                    "tests/smoke/kapt/kotlin/AutoValue_TestKtValue.class",
+                    "tests/smoke/kapt/java/TestAutoValue\$Builder.class"
+            )
+        }
+        jarTestCase("ap_kotlin_resources.jar", description = "annotation processed artifacts should contain resources") {
+            assertContainsEntries("META-INF/services/tests.smoke.kapt.kotlin.TestKtService")
+        }
     }
 
     @Test
-    fun noPluginProvided() = withTestCaseJar("ap_kotlin_mixed_no_plugin.jar") {
-        assertDoesNotContainEntries(
-                "tests/smoke/kapt/kotlin/AutoValue_TestKtValue.class",
-                "tests/smoke/kapt/java/TestAutoValue\$Builder.class"
-        )
+    fun testMixedModeAnnotationProcessing() {
+        jarTestCase("ap_kotlin_mixed.jar", description = "annotation processing should work for mixed mode targets") {
+            assertContainsEntries(
+                    "tests/smoke/kapt/kotlin/AutoValue_TestKtValue.class",
+                    "tests/smoke/kapt/java/TestAutoValue\$Builder.class"
+            )
+        }
+        jarTestCase("ap_kotlin_resources_mixed.jar", description = "annotation processors generating resources should work for mixed mode") {
+            assertContainsEntries("META-INF/services/tests.smoke.kapt.kotlin.TestKtService", "META-INF/services/tests.smoke.kapt.java.TestJavaService")
+        }
+        jarTestCase(
+                "ap_kotlin_mixed_inherit_plugin_via_exported_deps.jar",
+                description = "annotation processors should be inherited transitively"
+        ) {
+            assertContainsEntries(
+                    "META-INF/services/tests.smoke.kapt.kotlin.TestKtService",
+                    "META-INF/services/tests.smoke.kapt.java.TestJavaService",
+                    "tests/smoke/kapt/kotlin/AutoValue_TestKtValue.class",
+                    "tests/smoke/kapt/java/TestAutoValue\$Builder.class"
+            )
+        }
     }
 
     @Test
-    fun mixedMode() = withTestCaseJar("ap_kotlin_mixed.jar") {
-        assertContainsEntries(
-                "tests/smoke/kapt/kotlin/AutoValue_TestKtValue.class",
-                "tests/smoke/kapt/java/TestAutoValue\$Builder.class"
-        )
-    }
-
-    @Test
-    fun withResources() = withTestCaseJar("ap_kotlin_resources.jar") {
-        assertContainsEntries("META-INF/services/tests.smoke.kapt.kotlin.TestKtService")
-    }
-
-    @Test
-    fun mixedModeWithResources() = withTestCaseJar("ap_kotlin_resources_mixed.jar") {
-        assertContainsEntries(
-                "META-INF/services/tests.smoke.kapt.kotlin.TestKtService",
-                "META-INF/services/tests.smoke.kapt.java.TestJavaService"
-        )
-    }
-
-    @Test
-    fun withMultiplePlugins() = withTestCaseJar("ap_kotlin_mixed_multiple_plugins.jar") {
-        assertContainsEntries(
-                "META-INF/services/tests.smoke.kapt.kotlin.TestKtService",
-                "META-INF/services/tests.smoke.kapt.java.TestJavaService",
-                "tests/smoke/kapt/kotlin/AutoValue_TestKtValue.class",
-                "tests/smoke/kapt/java/TestAutoValue\$Builder.class"
-        )
-    }
-
-    @Test
-    fun withMultiplePluginsOneWithoutProcessorClassAttribute() = withTestCaseJar("ap_kotlin_mixed_multiple_plugins_one_without_processor_class.jar") {
-        assertContainsEntries("META-INF/services/tests.smoke.kapt.kotlin.TestKtService", "META-INF/services/tests.smoke.kapt.java.TestJavaService")
-        assertDoesNotContainEntries(
-                "tests/smoke/kapt/java/AutoValue_TestAPNoGenReferences.class",
-                "tests/smoke/kapt/kotlin/AutoValue_TestKtValueNoReferences.class"
-        )
-    }
-
-    @Test
-    fun withTransitivelyInheritedPlugin() = withTestCaseJar("ap_kotlin_mixed_inherit_plugin_via_exported_deps.jar") {
-        assertContainsEntries(
-                "META-INF/services/tests.smoke.kapt.kotlin.TestKtService",
-                "META-INF/services/tests.smoke.kapt.java.TestJavaService",
-                "tests/smoke/kapt/kotlin/AutoValue_TestKtValue.class",
-                "tests/smoke/kapt/java/TestAutoValue\$Builder.class"
-        )
+    fun testMultiPlugins() {
+        jarTestCase("ap_kotlin_mixed_multiple_plugins.jar", description = "annotation processing should work for multiple plugins") {
+            assertContainsEntries(
+                    "META-INF/services/tests.smoke.kapt.kotlin.TestKtService",
+                    "META-INF/services/tests.smoke.kapt.java.TestJavaService",
+                    "tests/smoke/kapt/kotlin/AutoValue_TestKtValue.class",
+                    "tests/smoke/kapt/java/TestAutoValue\$Builder.class"
+            )
+        }
+        jarTestCase("ap_kotlin_mixed_multiple_plugins_one_without_processor_class.jar",
+                description = "annotation processing should not work unless a processor class is provided") {
+            assertContainsEntries("META-INF/services/tests.smoke.kapt.kotlin.TestKtService", "META-INF/services/tests.smoke.kapt.java.TestJavaService")
+            assertDoesNotContainEntries(
+                    "tests/smoke/kapt/java/AutoValue_TestAPNoGenReferences.class",
+                    "tests/smoke/kapt/kotlin/AutoValue_TestKtValueNoReferences.class"
+            )
+        }
     }
 
     @Test
     fun daggerExampleIsRunnable() {
-        assertExecutableRunfileSucceeds("//examples/dagger/coffee_app")
+        assertExecutableRunfileSucceeds("//examples/dagger/coffee_app", description = "the dagger coffee_app should execute succesfully")
     }
 }
\ No newline at end of file
diff --git a/tests/integrationtests/jvm/JvmBasicIntegrationTests.kt b/tests/integrationtests/jvm/JvmBasicIntegrationTests.kt
index f7da49a..e20afe9 100644
--- a/tests/integrationtests/jvm/JvmBasicIntegrationTests.kt
+++ b/tests/integrationtests/jvm/JvmBasicIntegrationTests.kt
@@ -20,46 +20,49 @@
 
 
 class JvmBasicIntegrationTests : AssertionTestCase("tests/integrationtests/jvm/basic") {
-    // Resources tests /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     @Test
-    fun mergeResourceJars() = withTestCaseJar("test_merge_resourcesjar.jar") {
-        assertContainsEntries("testresources/AClass.class", "testresources/BClass.class", "pkg/file.txt")
+    fun testResourceMerging() {
+        jarTestCase("test_embed_resources.jar", description = "The rules should support including resource directories") {
+            assertContainsEntries(
+                    "testresources/AClass.class",
+                    "testresources/BClass.class",
+                    "tests/integrationtests/jvm/basic/testresources/resources/one/alsoAFile.txt",
+                    "tests/integrationtests/jvm/basic/testresources/resources/one/two/aFile.txt"
+            )
+        }
+        jarTestCase("test_merge_resourcesjar.jar", description = "the rules should support merging jars") {
+            assertContainsEntries("testresources/AClass.class", "testresources/BClass.class", "pkg/file.txt")
+        }
+        jarTestCase("test_embed_resources_strip_prefix.jar", description = "the rules should support the resource_strip_prefix attribute") {
+            assertContainsEntries("testresources/AClass.class", "testresources/BClass.class", "one/two/aFile.txt", "one/alsoAFile.txt")
+        }
+        jarTestCase("conventional_strip_resources.jar", description = "the rules should support conventional prefix stripping") {
+            assertContainsEntries("main.txt", "test.txt")
+        }
     }
 
     @Test
-    fun embedResources() = withTestCaseJar("test_embed_resources.jar") {
-        assertContainsEntries(
-                "testresources/AClass.class",
-                "testresources/BClass.class",
-                "tests/integrationtests/jvm/basic/testresources/resources/one/alsoAFile.txt",
-                "tests/integrationtests/jvm/basic/testresources/resources/one/two/aFile.txt"
+    fun testPropogateDeps() {
+        assertExecutableRunfileSucceeds(
+                "propagation_rt_via_export_consumer",
+                description = "Runtime deps should be inherited transitively from `exported` deps"
+        )
+        assertExecutableRunfileSucceeds(
+                "propagation_rt_via_runtime_deps_consumer",
+                description = "Runtime deps should be inherited transitively from `runtime_deps`"
         )
     }
 
     @Test
-    fun resourcesStripPrefix() = withTestCaseJar("test_embed_resources_strip_prefix.jar") {
-        assertContainsEntries("testresources/AClass.class", "testresources/BClass.class", "one/two/aFile.txt", "one/alsoAFile.txt")
-    }
-
-    @Test
-    fun resourcesHaveConventionPathsStripped() = withTestCaseJar("conventional_strip_resources.jar") { assertContainsEntries("main.txt", "test.txt") }
-
-    // Dependency propogation tests ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-    @Test
-    fun runtimeDepsArePropogatedViaExport() = assertExecutableRunfileSucceeds("propagation_rt_via_export_consumer")
-
-    @Test
-    fun runtimeDepsArePropogatedTransitively() = assertExecutableRunfileSucceeds("propagation_rt_via_runtime_deps_consumer")
-
-    // Module naming tests /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-    @Test
-    fun moduleNameIsDervidedOnBin() = withTestCaseJar("test_module_name_bin.jar") {
-        assertContainsEntries("META-INF/tests_integrationtests_jvm_basic-test_module_name_bin.kotlin_module")
-    }
-
-    @Test
-    fun moduleNameIsUsedOnBin() = withTestCaseJar("test_module_name_attr_bin.jar") {
-        assertContainsEntries("META-INF/hello-module.kotlin_module")
+    fun testModuleNaming() {
+        jarTestCase("test_module_name_bin.jar", description = "A binary rule should support default module naming") {
+            assertContainsEntries("META-INF/tests_integrationtests_jvm_basic-test_module_name_bin.kotlin_module")
+        }
+        jarTestCase("test_module_name_lib.jar", description = "A library rule should support default module naming") {
+            assertContainsEntries("META-INF/tests_integrationtests_jvm_basic-test_module_name_lib.kotlin_module")
+        }
+        jarTestCase("test_module_name_attr_lib.jar", description = "The kotlin rules should support the module_name attribute") {
+            assertContainsEntries("META-INF/hello-module.kotlin_module")
+        }
     }
 }
diff --git a/tests/integrationtests/jvm/basic/BUILD b/tests/integrationtests/jvm/basic/BUILD
index 24a9fcd..7a534ab 100644
--- a/tests/integrationtests/jvm/basic/BUILD
+++ b/tests/integrationtests/jvm/basic/BUILD
@@ -20,7 +20,12 @@
 )
 
 kt_jvm_library(
-    name="test_module_name_attr_bin",
+    name = "test_module_name_lib",
+    srcs = ["helloworld/Main.kt"]
+)
+
+kt_jvm_library(
+    name="test_module_name_attr_lib",
     srcs=glob(["helloworld/Main.kt"]),
     module_name = "hello-module"
 )
@@ -97,7 +102,8 @@
     name="cases",
     srcs = [
         ":test_module_name_bin",
-        ":test_module_name_attr_bin",
+        ":test_module_name_lib",
+        ":test_module_name_attr_lib",
         ":test_embed_resources.jar",
         ":test_merge_resourcesjar.jar",
         ":test_embed_resources_strip_prefix.jar",
diff --git a/tests/integrationtests/lib/AssertionTestCase.kt b/tests/integrationtests/lib/AssertionTestCase.kt
index a1baf36..1366e43 100644
--- a/tests/integrationtests/lib/AssertionTestCase.kt
+++ b/tests/integrationtests/lib/AssertionTestCase.kt
@@ -20,6 +20,10 @@
 import java.nio.file.Paths
 import java.util.concurrent.TimeUnit
 import java.util.jar.JarFile
+import kotlin.test.fail
+
+class TestCaseFailedException(description: String? = null, ex: Throwable):
+        AssertionError(""""$description" failed, error: ${ex.message}""", ex)
 
 abstract class AssertionTestCase(root: String) {
     private val testRunfileRoot: Path = Paths.get(root).also {
@@ -29,21 +33,31 @@
         }
     }
 
+    private inline fun runTestCase(description: String? = null, op: () -> Unit) =
+            try { op() }
+            catch(t: Throwable) {
+                when(t) {
+                    is AssertionError -> throw TestCaseFailedException(description, t)
+                    is Exception -> throw TestCaseFailedException(description, t)
+                    else -> throw t
+                }
+            }
+
     private fun testCaseJar(jarName: String) = testRunfileRoot.resolve(jarName).toFile().let {
         check(it.exists()) { "jar $jarName did not exist in test case root $testRunfileRoot" }
         JarFile(it)
     }
 
-    protected fun withTestCaseJar(name: String, op: JarFile.() -> Unit) {
-        testCaseJar(name).also { op(it) }
-    }
+    private fun jarTestCase(name: String, op: JarFile.() -> Unit) { testCaseJar(name).also { op(it) } }
+    protected fun jarTestCase(name: String, description: String? = null, op: JarFile.() -> Unit) { runTestCase(description, { jarTestCase(name, op) }) }
+
 
     protected fun JarFile.assertContainsEntries(vararg entries: String) {
-        entries.forEach { assert(this.getJarEntry(it) != null) { "jar ${this.name} did not contain entry $it" } }
+        entries.forEach { if(this.getJarEntry(it) == null) { fail("jar ${this.name} did not contain entry $it") } }
     }
 
     protected fun JarFile.assertDoesNotContainEntries(vararg entries: String) {
-        entries.forEach { assert(this.getJarEntry(it) == null) { "jar ${this.name} contained entry $it" } }
+        entries.forEach { if(this.getJarEntry(it) != null) { fail("jar ${this.name} contained entry $it") } }
     }
 
     private fun String.resolveDirectory(): File =
@@ -52,12 +66,14 @@
             else
                 testRunfileRoot.toFile()
 
-    protected fun assertExecutableRunfileSucceeds(executable: String) {
+    protected fun assertExecutableRunfileSucceeds(executable: String, description: String? = null) {
         ProcessBuilder().command("bash", "-c", Paths.get(executable).fileName.toString())
                 .also { it.directory(executable.resolveDirectory()) }
                 .start().let {
             it.waitFor(5, TimeUnit.SECONDS)
-            assert(it.exitValue() == 0) { "non-zero status code: ${it.exitValue()}" }
+            assert(it.exitValue() == 0) {
+                throw TestCaseFailedException(description, RuntimeException("non-zero return code: ${it.exitValue()}"))
+            }
         }
     }
 }
\ No newline at end of file