From c15097213682b91a653cdd91f328fa45fcb72401 Mon Sep 17 00:00:00 2001 From: Phil Date: Mon, 13 Dec 2021 19:28:23 -0700 Subject: [PATCH 1/2] refactor common code from scripting tests; invalid tests fail by default --- .../tools/scripting/BashScriptsTests.scala | 138 ++--------- .../tools/scripting/ClasspathTests.scala | 115 +-------- .../dotty/tools/scripting/ScriptTestEnv.scala | 225 ++++++++++++++++++ .../tools/scripting/ScriptingTests.scala | 78 +++--- 4 files changed, 283 insertions(+), 273 deletions(-) create mode 100644 compiler/test/dotty/tools/scripting/ScriptTestEnv.scala diff --git a/compiler/test/dotty/tools/scripting/BashScriptsTests.scala b/compiler/test/dotty/tools/scripting/BashScriptsTests.scala index 759430a26817..0e7f7238616f 100644 --- a/compiler/test/dotty/tools/scripting/BashScriptsTests.scala +++ b/compiler/test/dotty/tools/scripting/BashScriptsTests.scala @@ -2,29 +2,28 @@ package dotty package tools package scripting -import java.io.File -import java.nio.file.{Path, Paths, Files} -import scala.sys.process._ - import org.junit.Test import org.junit.Assert.assertEquals import vulpix.TestConfiguration -import dotty.tools.dotc.config.Properties._ +import ScriptTestEnv.* /** Verifies correct handling of command line arguments by `dist/bin/scala` and `dist/bin/scalac`. * +. arguments following a script path must be treated as script arguments * +. preserve script command line arguments. + * +. prevent SCALA_OPTS in build environment from infecting tests, via 'SCALA_OPTS= ' prefix + * +. test scripts must not throw execptions or exit with nonzero. */ class BashScriptsTests: // classpath tests managed by scripting.ClasspathTests.scala def testFiles = scripts("/scripting") + lazy val argsfile = createArgsFile() // avoid problems caused by drive letter printf("osname[%s]\n", osname) - printf("using JAVA_HOME=%s\n", javaHome) - printf("using SCALA_HOME=%s\n", scalaHome) - printf("first 5 PATH entries:\n%s\n", pathEntries.take(5).mkString("\n")) + printf("using JAVA_HOME=%s\n", envJavaHome) + printf("using SCALA_HOME=%s\n", envScalaHome) + printf("first 5 PATH entries:\n%s\n", adjustedPathEntries.take(5).mkString("\n")) printf("scala path: [%s]\n", scalaPath) printf("scalac path: [%s]\n", scalacPath) @@ -44,9 +43,9 @@ class BashScriptsTests: /* verify `dist/bin/scalac` non-interference with command line args following script name */ @Test def verifyScalacArgs = - val commandline = (Seq(scalacPath, "-script", showArgsScript) ++ testScriptArgs).mkString(" ") + val commandline = (Seq("SCALA_OPTS= ", scalacPath, "-script", showArgsScript) ++ testScriptArgs).mkString(" ") val (validTest, exitCode, stdout, stderr) = bashCommand(commandline) - if validTest then + if verifyValid(validTest) then var fail = false printf("\n") for (line, expect) <- stdout zip expectedOutput do @@ -59,33 +58,33 @@ class BashScriptsTests: /* verify `dist/bin/scala` with -J setting */ @Test def verifyScJProperty = - val commandline = Seq(scalaPath, "-J-Dkey=World", testFiles.find(_.getName == "envtest.sc").get.absPath).mkString(" ") + val commandline = Seq("SCALA_OPTS= ", scalaPath, "-J-Dkey=World", testFiles.find(_.getName == "envtest.sc").get.absPath).mkString(" ") val (validTest, exitCode, stdout, stderr) = bashCommand(commandline) assertEquals(stdout.mkString("/n"), "Hello World") /* verify `dist/bin/scala` with -J setting */ @Test def verifyScalaJProperty = - val commandline = Seq(scalaPath, "-J-Dkey=World3", testFiles.find(_.getName == "envtest.scala").get.absPath).mkString(" ") + val commandline = Seq("SCALA_OPTS= ", scalaPath, "-J-Dkey=World3", testFiles.find(_.getName == "envtest.scala").get.absPath).mkString(" ") val (validTest, exitCode, stdout, stderr) = bashCommand(commandline) assertEquals(stdout.mkString("/n"), "Hello World3") /* verify `dist/bin/scala` with -D setting */ @Test def verifyScDProperty = - val commandline = Seq(scalaPath, "-Dkey=World3", testFiles.find(_.getName == "envtest.sc").get.absPath).mkString(" ") + val commandline = Seq("SCALA_OPTS= ", scalaPath, "-Dkey=World3", testFiles.find(_.getName == "envtest.sc").get.absPath).mkString(" ") val (validTest, exitCode, stdout, stderr) = bashCommand(commandline) assertEquals(stdout.mkString("/n"), "Hello World3") /* verify `dist/bin/scala` with -D setting */ @Test def verifyScalaDProperty = - val commandline = Seq(scalaPath, "-Dkey=World4", testFiles.find(_.getName == "envtest.scala").get.absPath).mkString(" ") + val commandline = Seq("SCALA_OPTS= ", scalaPath, "-Dkey=World4", testFiles.find(_.getName == "envtest.scala").get.absPath).mkString(" ") val (validTest, exitCode, stdout, stderr) = bashCommand(commandline) assertEquals(stdout.mkString("/n"), "Hello World4") /* verify `dist/bin/scala` with -D setting */ @Test def saveAndRunWithDProperty = - val commandline = Seq(scalaPath, "-save", testFiles.find(_.getName == "envtest.scala").get.absPath).mkString(" ") + val commandline = Seq("SCALA_OPTS= ", scalaPath, "-save", testFiles.find(_.getName == "envtest.scala").get.absPath).mkString(" ") val (_, _, _, _) = bashCommand(commandline) - val commandline2 = Seq(scalaPath, "-Dkey=World5", testFiles.find(_.getName == "envtest.jar").get.absPath).mkString(" ") + val commandline2 = Seq("SCALA_OPTS= ", scalaPath, "-Dkey=World5", testFiles.find(_.getName == "envtest.jar").get.absPath).mkString(" ") val (validTest, exitCode, stdout, stderr) = bashCommand(commandline2) assertEquals(stdout.mkString("/n"), "Hello World5") @@ -93,7 +92,7 @@ class BashScriptsTests: @Test def verifyScalaArgs = val commandline = (Seq("SCALA_OPTS= ", scalaPath, showArgsScript) ++ testScriptArgs).mkString(" ") val (validTest, exitCode, stdout, stderr) = bashCommand(commandline) - if validTest then + if verifyValid(validTest) then var fail = false printf("\n") var mismatches = List.empty[(String, String)] @@ -115,7 +114,7 @@ class BashScriptsTests: printf("===> verify valid system property script.path is reported by script [%s]\n", scriptFile.getName) printf("calling scriptFile: %s\n", scriptFile) val (validTest, exitCode, stdout, stderr) = bashCommand(scriptFile.absPath) - if validTest then + if verifyValid(validTest) then stdout.foreach { printf("stdout: [%s]\n", _) } stderr.foreach { printf("stderr: [%s]\n", _) } val valid = stdout.exists { _.endsWith(expected) } @@ -131,7 +130,7 @@ class BashScriptsTests: val envPairs = List(("SCALA_OPTS", s"@$argsfile")) val (validTest, exitCode, stdout, stderr) = bashCommand(scriptFile.absPath, envPairs) printf("stdout: %s\n", stdout.mkString("\n","\n","")) - if validTest then + if verifyValid(validTest) then val expected = s"${workingDirectory.norm}" val output = stdout.find( _.trim.startsWith("cwd") ).getOrElse("").dropWhile(_!=' ').trim printf("output [%s]\n", output) @@ -140,104 +139,3 @@ class BashScriptsTests: if valid then printf(s"\n===> success: classpath begins with %s, as reported by [%s]\n", workingDirectory, scriptFile.getName) assert(valid, s"script ${scriptFile.absPath} did not report valid java.class.path first entry") - def existingPath: String = envOrElse("PATH", "").norm - def adjustedPath = s"$javaHome/bin$psep$scalaHome/bin$psep$existingPath" - def pathEntries = adjustedPath.split(psep).toList - - lazy val argsfile = createArgsFile() // avoid problems caused by drive letter - def createArgsFile(): String = - val utfCharset = java.nio.charset.StandardCharsets.UTF_8.name - val path = Files.createTempFile("scriptingTest", ".args") - val text = s"-classpath ${workingDirectory.absPath}" - Files.write(path, text.getBytes(utfCharset)) - path.toFile.getAbsolutePath.norm - - def fixHome(s: String): String = - s.startsWith("~") match { - case false => s - case true => s.replaceFirst("~", userHome) - } - - extension(s: String) { - def toPath: Path = Paths.get(fixHome(s)) // .toAbsolutePath - def toFile: File = s.toPath.toFile - def absPath: String = s.toFile.absPath - def norm: String = s.replace('\\', '/') // bash expects forward slash - def isFile: Boolean = s.toFile.isFile - def exists: Boolean = s.toPath.toFile.exists - def name: String = s.toFile.getName - def dropExtension: String = s.reverse.dropWhile(_ != '.').drop(1).reverse - def parent(up: Int): String = s.norm.split("/").reverse.drop(up).reverse.mkString("/") - } - - extension(p: Path) { - def listFiles: Seq[File] = p.toFile.listFiles.toList - def norm: String = p.normalize.toString.replace('\\', '/') - def name: String = p.toFile.getName - } - - extension(f: File) { - def name = f.getName - def norm: String = f.toPath.normalize.norm - def absPath: String = f.getAbsolutePath.norm - } - - lazy val psep: String = propOrElse("path.separator", "") - lazy val osname = propOrElse("os.name", "").toLowerCase - - lazy val scalacPath = s"$workingDirectory/dist/target/pack/bin/scalac".norm - lazy val scalaPath = s"$workingDirectory/dist/target/pack/bin/scala".norm - - // use optional working directory TEST_CWD, if defined - lazy val workingDirectory: String = envOrElse("TEST_CWD", userDir) - - // use optional TEST_BASH if defined, otherwise, bash must be in PATH - lazy val bashExe: String = envOrElse("TEST_BASH", whichBash) - - // test env SCALA_HOME is: - // dist/target/pack, if present - // else, SCALA_HOME if defined - // else, not defined - lazy val scalaHome = - if scalacPath.isFile then scalacPath.replaceAll("/bin/scalac", "") - else envOrElse("SCALA_HOME", "").norm - - lazy val javaHome = whichJava.parent(2) - - lazy val testEnvPairs = List( - ("JAVA_HOME", javaHome), - ("SCALA_HOME", scalaHome), - ("PATH", adjustedPath), - ).filter { case (name, valu) => valu.nonEmpty } - - lazy val whichBash: String = whichExe("bash") - lazy val whichJava: String = whichExe("java") - - def whichExe(basename: String): String = - val exeName = if (osname.toLowerCase.startsWith("windows")) s"$basename.exe" else basename - which(exeName) - - def bashCommand(cmdstr: String, additionalEnvPairs: List[(String, String)] = Nil): (Boolean, Int, Seq[String], Seq[String]) = { - var (stdout, stderr) = (List.empty[String], List.empty[String]) - if bashExe.toFile.exists then - val cmd = Seq(bashExe, "-c", cmdstr) - val envPairs = testEnvPairs ++ additionalEnvPairs - val proc = Process(cmd, None, envPairs *) - val exitVal = proc ! ProcessLogger ( - (out: String) => stdout ::= out, - (err: String) => stderr ::= err - ) - val validTest = exitVal == 0 && ! stderr.exists(_.contains("Permission denied")) - if ! validTest then - printf("\nunable to execute script, return value is %d\n", exitVal) - stderr.foreach { System.err.printf("stderr [%s]\n", _) } - (validTest, exitVal, stdout.reverse, stderr.reverse) - else - (false, -1, Nil, Nil) - } - - def execCmd(command: String, options: String *): Seq[String] = - val cmd = (command :: options.toList).toSeq - for { - line <- Process(cmd).lazyLines_! - } yield line diff --git a/compiler/test/dotty/tools/scripting/ClasspathTests.scala b/compiler/test/dotty/tools/scripting/ClasspathTests.scala index d755789543a3..976ca0172f20 100755 --- a/compiler/test/dotty/tools/scripting/ClasspathTests.scala +++ b/compiler/test/dotty/tools/scripting/ClasspathTests.scala @@ -2,25 +2,12 @@ package dotty package tools package scripting -import java.io.File -import java.nio.file.{Files, Paths, Path} - import org.junit.Test - import vulpix.TestConfiguration - -import scala.sys.process._ -import scala.jdk.CollectionConverters._ -import dotty.tools.dotc.config.Properties._ +import ScriptTestEnv.* /** Test java command line generated by bin/scala and bin/scalac */ class ClasspathTests: - val packBinDir = "dist/target/pack/bin" - val packLibDir = "dist/target/pack/lib" - - def exists(scriptPath: Path): Boolean = Files.exists(scriptPath) - def packBinScalaExists:Boolean = exists(Paths.get(s"$packBinDir/scala")) - /* * verify classpath reported by called script. */ @@ -64,7 +51,7 @@ class ClasspathTests: */ @Test def unglobClasspathVerifyTest = { val testScriptName = "unglobClasspath.sc" - val testScript = scripts("/scripting").find { _.getName.matches(testScriptName) } match + val testScript = scripts("/scripting").find { _.name.matches(testScriptName) } match case None => sys.error(s"test script not found: ${testScriptName}") case Some(file) => file @@ -81,105 +68,9 @@ class ClasspathTests: // test script reports the classpath it sees val scriptOutput = exec(cmd:_*) val scriptCp = findTaggedLine("unglobbed classpath", scriptOutput) + printf("%s\n", scriptCp) val classpathJars = scriptCp.split(psep).map { _.getName }.sorted.distinct //classpathJars.foreach { printf("%s\n", _) } assert(classpathJars.size > 1) } - -//////////////// end of tests //////////////// -lazy val cwd = Paths.get(".").toAbsolutePath -lazy val wildcardEntry = "dist/target/pack/lib/*" - -def listJars(dir: String) = - val packlibDir = Paths.get(dir).toFile - if packlibDir.isDirectory then - packlibDir.listFiles.toList.map { _.getName }.filter { _.endsWith(".jar") } - else - Nil - -import scala.jdk.CollectionConverters._ -lazy val env:Map[String, String] = System.getenv.asScala.toMap - -// script output expected as ": " -def findTaggedLine(tag: String, lines: Seq[String]): String = - lines.find { _.startsWith(tag) } match - case None => - lines.foreach { System.err.printf("line[%s]\n", _) } - sys.error(s"no $tag: found in script output") - case Some(cwd) => cwd.dropWhile( _ != ' ').trim // discard tag - -def exec(cmd: String *): Seq[String] = Process(cmd).lazyLines_!.toList - -def which(str:String) = - var out = "" - path.find { entry => - val it = Paths.get(entry).toAbsolutePath - it.toFile.isDirectory && { - var testpath = s"$it/$str".norm - val test = Paths.get(testpath) - if test.toFile.exists then - out = testpath - true - else - val test = Paths.get(s"$it/$str.exe".norm) - if test.toFile.exists then - out = testpath - true - else - false - } - } - - out - -def bashExe = which("bash") -def unameExe = which("uname") -def path = envOrElse("PATH", "").split(psep).toList -def psep = sys.props("path.separator") - -def cygwin = ostype == "cygwin" -def mingw = ostype == "mingw" -def msys = ostype == "msys" -def winshell = cygwin || mingw || msys - -def ostypeFull = if unameExe.nonEmpty then exec(unameExe).mkString else "" -def ostype = ostypeFull.toLowerCase.takeWhile{ cc => cc >= 'a' && cc <='z' || cc >= 'A' && cc <= 'Z' } - -extension(p:Path) - def relpath: Path = cwd.relativize(p) - def norm: String = p.toString.replace('\\', '/') - -extension(path: String) - def getName: String = norm.replaceAll(".*/", "") - - // Normalize path separator, convert relative path to absolute - def norm: String = - path.replace('\\', '/') match { - case s if s.secondChar == ":" => s // .drop(2) // path without drive letter - case s if s.startsWith("./") => s.drop(2) - case s => s - } - - def parent: String = norm.replaceAll("/[^/]*$", "") - - // convert to absolute path relative to cwd. - def absPath: String = norm match - case str if str.isAbsolute => norm - case _ => Paths.get(userDir, norm).toString.norm - - def isDir: Boolean = Files.isDirectory(Paths.get(path)) - - def toUrl: String = Paths.get(absPath).toUri.toURL.toString - - // Treat norm paths with a leading '/' as absolute. - // Windows java.io.File#isAbsolute treats them as relative. - def isAbsolute = path.norm.startsWith("/") || (isWin && path.secondChar == ":") - def secondChar: String = path.take(2).drop(1).mkString("") - -extension (str: String) def dropExtension = - str.reverse.dropWhile(_ != '.').drop(1).reverse - -//extension(f: File) def absPath = -//f.getAbsolutePath.replace('\\', '/') - diff --git a/compiler/test/dotty/tools/scripting/ScriptTestEnv.scala b/compiler/test/dotty/tools/scripting/ScriptTestEnv.scala new file mode 100644 index 000000000000..b6b428288699 --- /dev/null +++ b/compiler/test/dotty/tools/scripting/ScriptTestEnv.scala @@ -0,0 +1,225 @@ + +package dotty +package tools +package scripting + +import java.io.File +import java.nio.file.{Path, Paths, Files} + +import dotty.tools.dotc.config.Properties.* + +import scala.sys.process.* +import scala.jdk.CollectionConverters.* + +/** + */ +object ScriptTestEnv { + def osname: String = sys.props("os.name").toLowerCase + def psep: String = sys.props("path.separator") + def userDir: String = sys.props("user.dir").norm + + def whichJava: String = whichExe("java") + def whichBash: String = whichExe("bash") + + def workingDirectory: String = envOrElse("TEST_CWD", userDir) // optional working directory TEST_CWD + + def envPath: String = envOrElse("PATH", "").norm + def adjustedPath: String = s"$envJavaHome/bin$psep$envScalaHome/bin$psep$envPath" + def adjustedPathEntries: List[String] = adjustedPath.split(psep).toList + def envPathEntries: List[String] = envPath.split(psep).toList + + def bashExe: String = envOrElse("TEST_BASH", whichBash) + + def unameExe = which("uname") + def ostypeFull = if unameExe.nonEmpty then exec(unameExe).mkString else "" + def ostype = ostypeFull.toLowerCase.takeWhile{ cc => cc >= 'a' && cc <='z' || cc >= 'A' && cc <= 'Z' } + + def cygwin = ostype == "cygwin" + def mingw = ostype == "mingw" + def msys = ostype == "msys" + def winshell = cygwin || mingw || msys + + def which(str: String) = + var out = "" + // use of adjusted path here would result in circular reference + envPathEntries.find { entry => + val it = Paths.get(entry).toAbsolutePath + it.toFile.isDirectory && { + var testpath = s"$it/$str".norm + val test = Paths.get(testpath) + if test.toFile.exists then + out = testpath + true + else + val test = Paths.get(s"$it/$str.exe".norm) + if test.toFile.exists then + out = testpath + true + else + false + } + } + out + + def whichExe(basename: String): String = + val exeName = if (osname.toLowerCase.startsWith("windows")) s"$basename.exe" else basename + which(exeName) + + def bashCommand(cmdstr: String, additionalEnvPairs: List[(String, String)] = Nil): (Boolean, Int, Seq[String], Seq[String]) = { + var (stdout, stderr) = (List.empty[String], List.empty[String]) + if bashExe.toFile.exists then + val cmd = Seq(bashExe, "-c", cmdstr) + val envPairs = testEnvPairs ++ additionalEnvPairs + val proc = Process(cmd, None, envPairs *) + val exitVal = proc ! ProcessLogger ( + (out: String) => stdout ::= out, + (err: String) => stderr ::= err + ) + // val validTest = exitVal == 0 && ! stderr.exists(_.contains("Permission denied")) + val validTest = !stderr.exists(_.contains("Permission denied")) + if ! validTest then + printf("\nunable to execute script, return value is %d\n", exitVal) + stderr.foreach { System.err.printf("stderr [%s]\n", _) } + + (validTest, exitVal, stdout.reverse, stderr.reverse) + else + (false, -1, Nil, Nil) + } + + def execCmd(command: String, options: String *): Seq[String] = + val cmd = (command :: options.toList).toSeq + for { + line <- Process(cmd).lazyLines_! + } yield line + + + def packBinDir = "dist/target/pack/bin" + def packLibDir = "dist/target/pack/lib" + def packBinScalaExists: Boolean = Files.exists(Paths.get(s"$packBinDir/scala")) + + def listJars(dir: String) = + val packlibDir = Paths.get(dir).toFile + if packlibDir.isDirectory then + packlibDir.listFiles.toList.map { _.getName }.filter { _.endsWith(".jar") } + else + Nil + + // script output expected as ": " + def findTaggedLine(tag: String, lines: Seq[String]): String = + lines.find { _.startsWith(tag) } match + case None => + lines.foreach { System.err.printf("line[%s]\n", _) } + sys.error(s"no $tag: found in script output") + case Some(cwd) => cwd.dropWhile( _ != ' ').trim // discard tag + + def exec(cmd: String *): Seq[String] = Process(cmd).lazyLines_!.toList + + def script2jar(scriptFile: File) = + val jarName = s"${scriptFile.getName.dropExtension}.jar" + File(scriptFile.getParent, jarName) + + def showScriptUnderTest(scriptFile: File): Unit = + printf("===> test script name [%s]\n", scriptFile.getName) + + def callExecutableJar(script: File, jar: File, scriptArgs: Array[String] = Array.empty[String]) = { + import scala.sys.process._ + val cmd = Array("java", s"-Dscript.path=${script.getName}", "-jar", jar.absPath) + ++ scriptArgs + Process(cmd).lazyLines_!.foreach { println } + } + + //////////////////////////////////////////////////////////////////////////////// + + def createArgsFile(): String = + val utfCharset = java.nio.charset.StandardCharsets.UTF_8.name + val path = Files.createTempFile("scriptingTest", ".args") + val text = s"-classpath ${workingDirectory.absPath}" + Files.write(path, text.getBytes(utfCharset)) + path.toFile.getAbsolutePath.norm + + def fixHome(s: String): String = + s.startsWith("~") match { + case false => s + case true => s.replaceFirst("~", userHome) + } + + extension(s: String) { + def norm: String = s.replace('\\', '/') // bash expects forward slash + def noDrive = if s.secondChar == ":" then s.drop(2).norm else s.norm + def toPath: Path = Paths.get(fixHome(s.noDrive)) // .toAbsolutePath + def toFile: File = new File(s) + def absPath: String = s.toFile.absPath + def isFile: Boolean = s.toFile.isFile + def isDirectory: Boolean = s.toFile.isDirectory + def exists: Boolean = s.toFile.exists + def name: String = s.toFile.getName + def getName: String = s.toFile.getName + def dropExtension: String = s.reverse.dropWhile(_ != '.').drop(1).reverse + def parent(up: Int): String = s.norm.split("/").reverse.drop(up).reverse.mkString("/") + def secondChar: String = s.take(2).drop(1).mkString("") + } + + extension(p: Path) { + def norm: String = p.normalize.toString.replace('\\', '/') + + def noDrive = p.norm match { + case str if str.drop(1).take(1) == ":" => str.drop(2) + case str => str + } + def name: String = p.toFile.getName + def relpath: Path = cwd.relativize(p) + def files: Seq[File] = p.toFile.files + def parent: String = norm.replaceAll("/[^/]*$", "") + + // convert to absolute path relative to cwd. + def absPath: String = if (p.isAbsolute) p.norm else Paths.get(userDir, p.norm).toString.norm + + def isDir: Boolean = Files.isDirectory(p) + + def toUrl: String = Paths.get(absPath).toUri.toURL.toString + + // Treat norm paths with a leading '/' as absolute (Windows java.io.File#isAbsolute treats them as relative) + def isAbsolute = p.norm.startsWith("/") || (isWin && p.norm.secondChar == ":") + } + + extension(f: File) { + def name = f.getName + def norm: String = f.toPath.normalize.norm + def absPath: String = f.getAbsolutePath.norm + def files: Seq[File] = f.listFiles.toList + } + + lazy val cwd: Path = Paths.get(".").toAbsolutePath + + lazy val scalacPath = s"$workingDirectory/dist/target/pack/bin/scalac".norm + lazy val scalaPath = s"$workingDirectory/dist/target/pack/bin/scala".norm + + // use optional TEST_BASH if defined, otherwise, bash must be in PATH + + // envScalaHome is: + // dist/target/pack, if present + // else, SCALA_HOME if defined + // else, not defined + lazy val envScalaHome = + if scalacPath.isFile then scalacPath.replaceAll("/bin/scalac", "") + else envOrElse("SCALA_HOME", "").norm + + lazy val envJavaHome: String = envOrElse("JAVA_HOME", whichJava.parent(2)).norm + + lazy val testEnvPairs = List( + ("JAVA_HOME", envJavaHome), + ("SCALA_HOME", envScalaHome), + ("PATH", adjustedPath), + ).filter { case (name, valu) => valu.nonEmpty } + + // if unable to execute bash commands, this prevents invalid tests from failing + lazy val passInvalidTests = envOrElse("PASS_INVALID_TESTS", "").nonEmpty + + def verifyValid(validTest: Boolean): Boolean = + // !validTest implies unable to execute scripts via bash (e.g., permissions, or bash not found, etc.) + if !validTest && !passInvalidTests then + assert(validTest == true, s"unable to call script via bash -c") + + validTest + +} diff --git a/compiler/test/dotty/tools/scripting/ScriptingTests.scala b/compiler/test/dotty/tools/scripting/ScriptingTests.scala index 922564d4ff58..7ddab2b55424 100644 --- a/compiler/test/dotty/tools/scripting/ScriptingTests.scala +++ b/compiler/test/dotty/tools/scripting/ScriptingTests.scala @@ -8,49 +8,13 @@ import java.nio.file.Path import org.junit.Test import vulpix.TestConfiguration - +import ScriptTestEnv.* /** Runs all tests contained in `compiler/test-resources/scripting/` */ class ScriptingTests: - extension (str: String) def dropExtension = - str.reverse.dropWhile(_ != '.').drop(1).reverse - - extension(f: File) def absPath = - f.getAbsolutePath.replace('\\', '/') - // classpath tests managed by scripting.ClasspathTests.scala def testFiles = scripts("/scripting").filter { ! _.getName.toLowerCase.contains("classpath") } - def script2jar(scriptFile: File) = - val jarName = s"${scriptFile.getName.dropExtension}.jar" - File(scriptFile.getParent, jarName) - - def showScriptUnderTest(scriptFile: File): Unit = - printf("===> test script name [%s]\n", scriptFile.getName) - - val argss: Map[String, Array[String]] = ( - for - argFile <- testFiles - if argFile.getName.endsWith(".args") - name = argFile.getName.dropExtension - scriptArgs = readLines(argFile).toArray - yield name -> scriptArgs).toMap - - def scalaFilesWithArgs(extension: String) = ( - for - scriptFile <- testFiles - if scriptFile.getName.endsWith(extension) - name = scriptFile.getName.dropExtension - scriptArgs = argss.getOrElse(name, Array.empty[String]) - yield scriptFile -> scriptArgs).toList.sortBy { (file, args) => file.getName } - - def callExecutableJar(script: File, jar: File, scriptArgs: Array[String] = Array.empty[String]) = { - import scala.sys.process._ - val cmd = Array("java", s"-Dscript.path=${script.getName}", "-jar", jar.absPath) - ++ scriptArgs - Process(cmd).lazyLines_!.foreach { println } - } - /* * Call .scala scripts without -save option, verify no jar created */ @@ -71,7 +35,7 @@ class ScriptingTests: printf("mainClass from ScriptingDriver: %s\n", mainClass) true // call compiled script main method } - assert(! unexpectedJar.exists, s"not expecting jar file: ${unexpectedJar.absPath}") + assert( !unexpectedJar.exists, s"not expecting jar file: ${unexpectedJar.absPath}" ) /* * Call .sc scripts without -save option, verify no jar created @@ -89,7 +53,7 @@ class ScriptingTests: ) ++ scriptArgs Main.main(mainArgs) - assert(! unexpectedJar.exists, s"not expecting jar file: ${unexpectedJar.absPath}") + assert( !unexpectedJar.exists, s"not expecting jar file: ${unexpectedJar.absPath}") /* * Call .sc scripts with -save option, verify jar is created. @@ -125,7 +89,7 @@ class ScriptingTests: // verify main method not called when false is returned printf("testing script compile, with no call to script main method.\n") touchedFile.delete - assert(!touchedFile.exists, s"unable to delete ${touchedFile}") + assert( !touchedFile.exists, s"unable to delete ${touchedFile}" ) ScriptingDriver( compilerArgs = Array("-classpath", TestConfiguration.basicClasspath), scriptFile = scriptFile, @@ -174,12 +138,44 @@ class ScriptingTests: assert(expectedJar.exists, s"unable to create executable jar [$expectedJar]") touchedFile.delete - assert(!touchedFile.exists, s"unable to delete ${touchedFile}") + assert( !touchedFile.exists, s"unable to delete ${touchedFile}" ) printf("calling executable jar %s\n", expectedJar) callExecutableJar(scriptFile, expectedJar) if touchedFile.exists then printf("success: executable jar created file %s\n", touchedFile) assert( touchedFile.exists, s"expected to find file ${touchedFile}" ) +/////////////////////////////////// def touchFileScript = testFiles.find(_.getName == "touchFile.sc").get + def touchedFile = File("touchedFile.out") + + def script2jar(scriptFile: File) = + val jarName = s"${scriptFile.getName.dropExtension}.jar" + File(scriptFile.getParent, jarName) + + def showScriptUnderTest(scriptFile: File): Unit = + printf("===> test script name [%s]\n", scriptFile.getName) + + def argss: Map[String, Array[String]] = ( + for + argFile <- testFiles + if argFile.getName.endsWith(".args") + name = argFile.getName.dropExtension + scriptArgs = readLines(argFile).toArray + yield name -> scriptArgs).toMap + + def scalaFilesWithArgs(extension: String) = ( + for + scriptFile <- testFiles + if scriptFile.getName.endsWith(extension) + name = scriptFile.getName.dropExtension + scriptArgs = argss.getOrElse(name, Array.empty[String]) + yield scriptFile -> scriptArgs).toList.sortBy { (file, args) => file.getName } + + def callExecutableJar(script: File, jar: File, scriptArgs: Array[String] = Array.empty[String]) = { + import scala.sys.process._ + val cmd = Array("java", s"-Dscript.path=${script.getName}", "-jar", jar.absPath) + ++ scriptArgs + Process(cmd).lazyLines_!.foreach { println } + } From 1da66ba74e27ceae3ca8473f6210ba0b8fe7f3f7 Mon Sep 17 00:00:00 2001 From: Phil Date: Mon, 13 Dec 2021 19:57:27 -0700 Subject: [PATCH 2/2] dump stdout/stderr if BashScriptsTests.verifyScalaOpts fails --- .../test/dotty/tools/scripting/BashScriptsTests.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/test/dotty/tools/scripting/BashScriptsTests.scala b/compiler/test/dotty/tools/scripting/BashScriptsTests.scala index 0e7f7238616f..08c3349f387b 100644 --- a/compiler/test/dotty/tools/scripting/BashScriptsTests.scala +++ b/compiler/test/dotty/tools/scripting/BashScriptsTests.scala @@ -132,10 +132,13 @@ class BashScriptsTests: printf("stdout: %s\n", stdout.mkString("\n","\n","")) if verifyValid(validTest) then val expected = s"${workingDirectory.norm}" - val output = stdout.find( _.trim.startsWith("cwd") ).getOrElse("").dropWhile(_!=' ').trim - printf("output [%s]\n", output) + val cwdline = stdout.find( _.trim.startsWith("cwd") ).getOrElse("") + printf("cwdline [%s]\n", cwdline) printf("expected[%s]\n", expected) - val valid = output.startsWith(expected) + val valid = cwdline.endsWith(expected) + if (!valid) then + stdout.foreach { printf("stdout[%s]\n", _) } + stderr.foreach { printf("stderr[%s]\n", _) } if valid then printf(s"\n===> success: classpath begins with %s, as reported by [%s]\n", workingDirectory, scriptFile.getName) assert(valid, s"script ${scriptFile.absPath} did not report valid java.class.path first entry")