diff --git a/third_party/dependency_analyzer/src/test/BUILD b/third_party/dependency_analyzer/src/test/BUILD index 3e09162a0..f002cb9ff 100644 --- a/third_party/dependency_analyzer/src/test/BUILD +++ b/third_party/dependency_analyzer/src/test/BUILD @@ -38,6 +38,24 @@ scala_test( ], ) +scala_test( + name = "scalac_dependency_test", + size = "small", + srcs = [ + "io/bazel/rulesscala/dependencyanalyzer/ScalacDependencyTest.scala", + ], + jvm_flags = common_jvm_flags, + unused_dependency_checker_mode = "off", + deps = [ + "//external:io_bazel_rules_scala/dependency/scala/scala_compiler", + "//external:io_bazel_rules_scala/dependency/scala/scala_library", + "//external:io_bazel_rules_scala/dependency/scala/scala_reflect", + "//third_party/dependency_analyzer/src/main:dependency_analyzer", + "//third_party/utils/src/test:test_util", + "@scalac_rules_commons_io//jar", + ], +) + scala_test( name = "strict_deps_test", size = "small", diff --git a/third_party/dependency_analyzer/src/test/io/bazel/rulesscala/dependencyanalyzer/ScalacDependencyTest.scala b/third_party/dependency_analyzer/src/test/io/bazel/rulesscala/dependencyanalyzer/ScalacDependencyTest.scala new file mode 100644 index 000000000..6126d9c2e --- /dev/null +++ b/third_party/dependency_analyzer/src/test/io/bazel/rulesscala/dependencyanalyzer/ScalacDependencyTest.scala @@ -0,0 +1,109 @@ +package third_party.dependency_analyzer.src.test.io.bazel.rulesscala.dependencyanalyzer + +import java.nio.file.Files +import java.nio.file.Path +import java.util.UUID +import org.apache.commons.io.FileUtils +import org.scalatest._ +import third_party.utils.src.test.io.bazel.rulesscala.utils.JavaCompileUtil +import third_party.utils.src.test.io.bazel.rulesscala.utils.TestUtil + +/** + * Test that the scalac compiler behaves how we expect it to around + * dependencies. That is, for given scenarios, we want to make sure + * that scalac requires the given set of dependencies; no more and + * no less. + * + * To clarify: these tests do not reflect the end result of strict/unused + * deps as we are considering alternatives of how to mitigate scalac's + * limitations. + */ +class ScalacDependencyTest extends FunSuite { + private def withSandbox(action: Sandbox => Unit): Unit = { + val tmpDir = Files.createTempDirectory("dependency_analyzer_test_temp") + val file = tmpDir.toFile + try { + action(new Sandbox(tmpDir)) + } finally { + FileUtils.deleteDirectory(file) + } + } + + private class Sandbox(tmpDir: Path) { + def compile( + code: String + ): Unit = { + val errors = + TestUtil.runCompiler( + code = code, + extraClasspath = List(tmpDir.toString), + outputPathOpt = Some(tmpDir) + ) + assert(errors.isEmpty) + } + + def compileJava( + className: String, + code: String + ): Unit = { + JavaCompileUtil.compile( + tmpDir = tmpDir.toString, + className = className, + code = code + ) + } + + def checkExactDepsNeeded( + code: String, + deps: List[String] + ): Unit = { + def doesCompileSucceed(usedDeps: List[String]): Boolean = { + val subdir = tmpDir.resolve(UUID.randomUUID().toString) + Files.createDirectory(subdir) + usedDeps.foreach { dep => + val name = s"$dep.class" + Files.copy(tmpDir.resolve(name), subdir.resolve(name)) + } + val errors = + TestUtil.runCompiler( + code = code, + extraClasspath = List(subdir.toString) + ) + errors.isEmpty + } + + assert(doesCompileSucceed(deps), s"Failed to compile with all deps") + + deps.foreach { toSkip => + val remaining = deps.filter(_ != toSkip) + // sanity check we removed exactly one item + assert(remaining.size + 1 == deps.size) + assert( + !doesCompileSucceed(remaining), + s"Compile succeeded even though $toSkip was missing") + } + } + } + + test("static annotation of superclass not needed") { + withSandbox { sandbox => + sandbox.compile("class A extends scala.annotation.StaticAnnotation") + sandbox.compile("@A class B") + sandbox.checkExactDepsNeeded( + code = "class C extends B", + deps = List("B") + ) + } + } + + test("superclass of superclass needed") { + withSandbox { sandbox => + sandbox.compile("class A") + sandbox.compile("class B extends A") + sandbox.checkExactDepsNeeded( + code = "class C extends B", + deps = List("A", "B") + ) + } + } +}