Run coverage in integration tests and drop coverage example (#1454)

* Run coverage in integration tests and drop coverage example

BazelIntegrationTestRunner now runs bazel coverage --combined_report=lcov for discovered *_test targets instead of a dedicated //:coverage_test.

Also removes shell-script hooks (test.sh / bash_path) and deletes the dedicated examples/coverage workspace.

* Windows fix

* Disable coverage run in integration tests on Windows
diff --git a/.bazelrc b/.bazelrc
index 472e393..40d7c5e 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -22,6 +22,12 @@
 build:windows --cxxopt=/Zc:__cplusplus
 build:windows --host_cxxopt=/Zc:__cplusplus
 
+# Avoid
+# Exception in thread "main" java.io.IOException: Error getting terminal size: GetConsoleScreenBufferInfo error 6
+# 	at coursier.jniutils.WindowsAnsiTerminal.terminalSize(WindowsAnsiTerminal.java:10)
+common:windows --repo_env=COURSIER_NO_TERM=true
+common:windows --repo_env=COURSIER_PROGRESS=false
+
 # To update these lines, execute
 # `bazel run @rules_bazel_integration_test//tools:update_deleted_packages`
 build --deleted_packages=examples/android,examples/android/app,examples/android/bzl,examples/android/libAndroid,examples/android/libJava,examples/android/libKtAndroid,examples/android/libKtAndroid/src/test/java/examples/android/lib,examples/android/third_party,examples/anvil,examples/anvil/app,examples/anvil/app/src/androidTest/java/com/squareup/anvil/sample,examples/anvil/app/src/main/java/com/squareup/anvil/sample,examples/anvil/app/src/test/java/com/squareup/anvil/sample,examples/anvil/library,examples/anvil/library/src/main/java/com/squareup/anvil/sample,examples/anvil/repro/src/main/java/com/repro/lib,examples/anvil/scopes,examples/anvil/scopes/src/main/java/com/squareup/scopes,examples/anvil/third_party,examples/associates,examples/associates/projects/core/api,examples/associates/projects/core/api/src/test/kotlin/core/api,examples/associates/projects/core/impl,examples/associates/projects/core/impl/src/test/kotlin/core/impl,examples/coverage,examples/dagger,examples/dagger/third_party,examples/deps,examples/deps/bzl,examples/deps/libAndroid1,examples/deps/libAndroid2,examples/deps/libAndroid3,examples/deps/libAndroid4,examples/deps/libJava1,examples/deps/libJava2,examples/deps/libJava3,examples/deps/libJava4,examples/deps/libKt1,examples/deps/libKt2,examples/deps/libKt3,examples/deps/libKt4,examples/deps/libKtAndroid1,examples/deps/libKtAndroid2,examples/deps/libKtAndroid3,examples/deps/libKtAndroid4,examples/jetpack_compose,examples/jetpack_compose/app,examples/jetpack_compose/compose-ui,examples/ksp,examples/ksp/third_party,examples/multiplex,examples/multiplex/src,examples/nested_module_resources,examples/nested_module_resources/nested,examples/plugin,examples/plugin/src/allopen,examples/plugin/src/allopennoarg,examples/plugin/src/noarg,examples/plugin/src/parcelize,examples/plugin/src/sam_with_receiver,examples/plugin/src/serialization,examples/trivial,examples/trivial/app
diff --git a/examples/coverage/BUILD.bazel b/examples/coverage/BUILD.bazel
deleted file mode 100644
index 183f4ca..0000000
--- a/examples/coverage/BUILD.bazel
+++ /dev/null
@@ -1,16 +0,0 @@
-load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library", "kt_jvm_test")
-
-kt_jvm_library(
-    name = "coverage_lib",
-    srcs = ["src/main/kotlin/com/example/coverage/SimpleKotlinLib.kt"],
-)
-
-kt_jvm_test(
-    name = "coverage_test",
-    srcs = ["src/test/kotlin/com/example/coverage/SimpleKotlinTest.kt"],
-    test_class = "com.example.coverage.SimpleKotlinTest",
-    deps = [
-        ":coverage_lib",
-        "@junit4//:junit_junit",
-    ],
-)
diff --git a/examples/coverage/MODULE.bazel b/examples/coverage/MODULE.bazel
deleted file mode 100644
index 760321d..0000000
--- a/examples/coverage/MODULE.bazel
+++ /dev/null
@@ -1,28 +0,0 @@
-module(
-    name = "coverage_example",
-    version = "1.0.0",
-)
-
-bazel_dep(name = "rules_java", version = "9.3.0")
-bazel_dep(name = "rules_kotlin", version = "")
-
-# Point to the local rules_kotlin
-local_path_override(
-    module_name = "rules_kotlin",
-    path = "../..",
-)
-
-bazel_dep(name = "rules_jvm_external", version = "6.9")
-
-# JUnit 4 dependencies
-maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
-maven.install(
-    name = "junit4",
-    artifacts = [
-        "junit:junit:4.13.2",
-    ],
-    repositories = [
-        "https://repo1.maven.org/maven2",
-    ],
-)
-use_repo(maven, "junit4")
diff --git a/examples/coverage/README.md b/examples/coverage/README.md
deleted file mode 100644
index b761a0c..0000000
--- a/examples/coverage/README.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# Coverage Example
-
-This example demonstrates Kotlin code coverage with Bazel using `bazel coverage`.
-
-## Background
-
-This example verifies the fix for [issue #1447](https://github.com/bazelbuild/rules_kotlin/issues/1447), which addresses a JaCoCo version mismatch between `rules_kotlin` and `rules_java 9.3.0+`.
-
-The issue occurred because the kotlin_worker.jar was compiled against JaCoCo 0.8.11, but `rules_java 9.3.0` uses JaCoCo 0.8.14. This caused a `NoClassDefFoundError` when running `bazel coverage` on Kotlin targets.
-
-## The Fix
-
-The fix reorders `runtime_deps` to put `@bazel_tools//tools/jdk:JacocoCoverage` **before** the worker. This ensures the newer JaCoCo classes from Bazel's tools are loaded first, taking precedence over the older version bundled in the worker.
-
-## Running the Example
-
-```bash
-cd examples/coverage
-bazel coverage --combined_report=lcov //:coverage_test
-```
-
-To view the HTML report:
-```bash
-genhtml bazel-out/_coverage/_coverage_report.dat -o coverage_html
-```
-
-## Expected Results
-
-The coverage test should run successfully without any `NoClassDefFoundError` exceptions, and generate coverage reports in `bazel-out/_coverage/`.
diff --git a/examples/coverage/src/main/kotlin/com/example/coverage/SimpleKotlinLib.kt b/examples/coverage/src/main/kotlin/com/example/coverage/SimpleKotlinLib.kt
deleted file mode 100644
index 968199b..0000000
--- a/examples/coverage/src/main/kotlin/com/example/coverage/SimpleKotlinLib.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.example.coverage
-
-class SimpleKotlinLib {
-    fun add(a: Int, b: Int): Int {
-        return if (a > 0) {
-            a + b
-        } else {
-            b
-        }
-    }
-    
-    fun multiply(a: Int, b: Int): Int {
-        return a * b
-    }
-}
diff --git a/examples/coverage/src/test/kotlin/com/example/coverage/SimpleKotlinTest.kt b/examples/coverage/src/test/kotlin/com/example/coverage/SimpleKotlinTest.kt
deleted file mode 100644
index ac14db9..0000000
--- a/examples/coverage/src/test/kotlin/com/example/coverage/SimpleKotlinTest.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.example.coverage
-
-import org.junit.Test
-import org.junit.Assert.assertEquals
-
-class SimpleKotlinTest {
-    @Test
-    fun testAdd() {
-        val lib = SimpleKotlinLib()
-        assertEquals(3, lib.add(1, 2))
-        assertEquals(2, lib.add(0, 2))
-    }
-    
-    @Test
-    fun testMultiply() {
-        val lib = SimpleKotlinLib()
-        assertEquals(6, lib.multiply(2, 3))
-    }
-}
diff --git a/examples/coverage/test.sh b/examples/coverage/test.sh
deleted file mode 100755
index bcb6f2e..0000000
--- a/examples/coverage/test.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-# Additional test script for coverage example
-# Runs after standard build/test cycle to verify coverage-specific functionality
-#
-# This script is executed by BazelIntegrationTestRunner using the bash binary
-# from Bazel's shell toolchain.
-#
-# Environment variables provided by BazelIntegrationTestRunner:
-#   BIT_BAZEL_BINARY: path to the bazel binary
-#   BIT_STARTUP_FLAGS: flags that go before the command (e.g., --bazelrc)
-#   BIT_COMMAND_FLAGS: flags that go after the command (e.g., --override_module, --enable_bzlmod)
-
-set -e
-
-BAZEL="${BIT_BAZEL_BINARY:-bazel}"
-
-echo "Running bazel coverage test..."
-# shellcheck disable=SC2086
-OUTPUT=$("$BAZEL" $BIT_STARTUP_FLAGS coverage $BIT_COMMAND_FLAGS --combined_report=lcov //:coverage_test 2>&1) || {
-    echo "Coverage test failed"
-    echo "$OUTPUT"
-    exit 1
-}
-
-# Check if the output contains NoClassDefFoundError for JaCoCo Offline class
-if echo "$OUTPUT" | grep -q "NoClassDefFoundError.*Offline"; then
-    echo "JaCoCo version mismatch error detected"
-    exit 1
-fi
-
-echo "Coverage test passed!"
diff --git a/src/main/kotlin/io/bazel/kotlin/test/BUILD.bazel b/src/main/kotlin/io/bazel/kotlin/test/BUILD.bazel
index afc2b1f..53656c1 100644
--- a/src/main/kotlin/io/bazel/kotlin/test/BUILD.bazel
+++ b/src/main/kotlin/io/bazel/kotlin/test/BUILD.bazel
@@ -1,7 +1,4 @@
 load("//kotlin:jvm.bzl", "kt_jvm_binary")
-load(":bash.bzl", "bash_path")
-
-bash_path(name = "bash_path")
 
 kt_jvm_binary(
     name = "BazelIntegrationTestRunner",
@@ -9,12 +6,10 @@
         "BazelIntegrationTestRunner.kt",
     ],
     data = [
-        ":bash_path",
         "//:rules_kotlin_release",
     ],
     jvm_flags = [
         "-D@rules_kotlin...rules_kotlin_release=$(rlocationpath //:rules_kotlin_release)",
-        "-Dio.bazel.kotlin.test.bash_path=$(rlocationpath :bash_path)",
     ],
     main_class = "io.bazel.kotlin.test.BazelIntegrationTestRunner",
     visibility = [
diff --git a/src/main/kotlin/io/bazel/kotlin/test/BazelIntegrationTestRunner.kt b/src/main/kotlin/io/bazel/kotlin/test/BazelIntegrationTestRunner.kt
index a5f0710..d31dc44 100644
--- a/src/main/kotlin/io/bazel/kotlin/test/BazelIntegrationTestRunner.kt
+++ b/src/main/kotlin/io/bazel/kotlin/test/BazelIntegrationTestRunner.kt
@@ -23,6 +23,7 @@
 object BazelIntegrationTestRunner {
   @JvmStatic
   fun main(args: Array<String>) {
+    val isWindows = System.getProperty("os.name").lowercase().contains("windows")
     val fs = FileSystems.getDefault()
     val bazel = fs.getPath(System.getenv("BIT_BAZEL_BINARY"))
     val workspace = fs.getPath(System.getenv("BIT_WORKSPACE_DIR"))
@@ -137,54 +138,38 @@
           *commandFlags,
           "kind(\".*_test\", \"//...\")",
         ).ok { process ->
-          if (process.stdOut.isNotEmpty()) {
-            bazel.run(
-              workspace,
-              *systemFlags,
-              "test",
-              *commandFlags,
-              "--test_output=all",
-              "//...",
-            ).onFailThrow()
-          }
+          process.stdOut.toString(UTF_8)
+            .lineSequence()
+            .map(String::trim)
+            .filter(String::isNotEmpty)
+            .toList()
+            .sorted()
         }
-
-        // Run test script if it exists
-        val testScript = workspace.resolve("test.sh")
-        if (testScript.exists()) {
-          val bashPathFile = BazelRunFiles.resolveVerifiedFromProperty(fs, "io.bazel.kotlin.test.bash_path")
-          val bash = Files.readString(bashPathFile).trim()
-          println("Running test script [${testScript.fileName}]...")
-          ProcessBuilder()
-            .command(bash, testScript.toString())
-            .directory(workspace.toFile())
-            .also { pb ->
-              pb.environment()["BIT_STARTUP_FLAGS"] = systemFlags.joinToString(" ")
-              pb.environment()["BIT_COMMAND_FLAGS"] = commandFlags.joinToString(" ")
-            }
-            .start()
-            .let { process ->
-              val executor = Executors.newCachedThreadPool()
-              try {
-                val stdOut = executor.submit(process.inputStream.streamTo(System.out))
-                val stdErr = executor.submit(process.errorStream.streamTo(System.out))
-                if (!process.waitFor(600, TimeUnit.SECONDS) || process.exitValue() != 0) {
-                  throw AssertionError(
-                    """
-                    Test script failed with exit code ${process.exitValue()}:
-                    stdout:
-                    ${stdOut.get().toString(UTF_8)}
-                    stderr:
-                    ${stdErr.get().toString(UTF_8)}
-                    """.trimIndent(),
-                  )
-                }
-              } finally {
-                executor.shutdown()
-                executor.awaitTermination(1, TimeUnit.SECONDS)
+          .also { testTargets ->
+            if (testTargets.isNotEmpty()) {
+              val coverageTargets = testTargets.toTypedArray()
+              bazel.run(
+                workspace,
+                *systemFlags,
+                "test",
+                *commandFlags,
+                "--test_output=all",
+                "//...",
+              ).onFailThrow()
+              if (isWindows) {
+                println("Skipping coverage on Windows integration runs.")
+              } else {
+                bazel.run(
+                  workspace,
+                  *systemFlags,
+                  "coverage",
+                  *commandFlags,
+                  "--combined_report=lcov",
+                  *coverageTargets,
+                ).onFailThrow()
               }
             }
-        }
+          }
       }
     }
   }
diff --git a/src/main/kotlin/io/bazel/kotlin/test/bash.bzl b/src/main/kotlin/io/bazel/kotlin/test/bash.bzl
deleted file mode 100644
index 53096b6..0000000
--- a/src/main/kotlin/io/bazel/kotlin/test/bash.bzl
+++ /dev/null
@@ -1,19 +0,0 @@
-"""Rule to expose the shell toolchain's bash binary path."""
-
-def _bash_path_impl(ctx):
-    toolchain = ctx.toolchains["@bazel_tools//tools/sh:toolchain_type"]
-    bash_path = toolchain.path
-
-    # Write the path to a file that can be read at runtime
-    path_file = ctx.actions.declare_file(ctx.label.name + ".path")
-    ctx.actions.write(
-        output = path_file,
-        content = bash_path,
-    )
-
-    return [DefaultInfo(files = depset([path_file]))]
-
-bash_path = rule(
-    implementation = _bash_path_impl,
-    toolchains = ["@bazel_tools//tools/sh:toolchain_type"],
-)