@@ -2,33 +2,38 @@ package dotty
22package tools
33package scripting
44
5- import java .io .File
6- import java .nio .file .{Path , Paths , Files }
7- import scala .sys .process ._
8-
9- import org .junit .Test
5+ import org .junit .{Test , AfterClass }
106import org .junit .Assert .assertEquals
117
128import vulpix .TestConfiguration
139
14- import dotty . tools . dotc . config . Properties . _
10+ import ScriptTestEnv . *
1511
1612/** Verifies correct handling of command line arguments by `dist/bin/scala` and `dist/bin/scalac`.
1713 * +. arguments following a script path must be treated as script arguments
1814 * +. preserve script command line arguments.
15+ * +. prevent SCALA_OPTS in build environment from infecting tests, via 'SCALA_OPTS= ' prefix
16+ * +. test scripts must not throw execptions or exit with nonzero.
1917 */
20- class BashScriptsTests :
21- // classpath tests managed by scripting.ClasspathTests.scala
18+ object BashScriptsTests :
19+ lazy val argsfile = createArgsFile() // avoid problems caused by drive letter
2220 def testFiles = scripts(" /scripting" )
2321
22+ @ AfterClass def cleanup : Unit = {
23+ val af = argsfile.toFile
24+ if (af.exists) {
25+ af.delete()
26+ }
27+ }
2428 printf(" osname[%s]\n " , osname)
25- printf(" using JAVA_HOME=%s\n " , javaHome)
26- printf(" using SCALA_HOME=%s\n " , scalaHome)
27- printf(" first 5 PATH entries:\n %s\n " , pathEntries.take(5 ).mkString(" \n " ))
29+ printf(" uname[%s]\n " , ostypeFull)
30+ printf(" using JAVA_HOME=%s\n " , envJavaHome)
31+ printf(" using SCALA_HOME=%s\n " , envScalaHome)
32+ printf(" first 5 PATH entries:\n %s\n " , adjustedPathEntries.take(5 ).mkString(" \n " ))
2833 printf(" scala path: [%s]\n " , scalaPath)
2934 printf(" scalac path: [%s]\n " , scalacPath)
3035
31- lazy val expectedOutput = List (
36+ val expectedOutput = List (
3237 " arg 0:[a]" ,
3338 " arg 1:[b]" ,
3439 " arg 2:[c]" ,
@@ -37,16 +42,88 @@ class BashScriptsTests:
3742 " arg 5:[-script]" ,
3843 " arg 6:[-debug]" ,
3944 )
40- lazy val testScriptArgs = Seq (
45+ val testScriptArgs = Seq (
4146 " a" , " b" , " c" , " -repl" , " -run" , " -script" , " -debug"
4247 )
4348 val showArgsScript = testFiles.find(_.getName == " showArgs.sc" ).get.absPath
4449
50+ def testFile (name : String ): String =
51+ val file = testFiles.find(_.getName == name) match {
52+ case Some (f) =>
53+ val ff = f.absPath
54+ printf(" test file [%s] is [%s]\n " , name, ff)
55+ ff
56+ case None =>
57+ printf(" test file [%s] not found!\n " , name)
58+ name.absPath
59+ }
60+ file
61+
62+ val Seq (envtestSc, envtestScala) = Seq (" envtest.sc" , " envtest.scala" ).map { testFile(_) }
63+
64+ // create command line with given options, execute specified script, return stdout
65+ def callScript (tag : String , script : String , keyPre : String ): String =
66+ val keyArg = s " $keyPre= $tag"
67+ printf(" pass tag [%s] via [%s] to script [%s]\n " , tag, keyArg, script)
68+ val cmd : String = Seq (" SCALA_OPTS= " , scalaPath, keyArg, script).mkString(" " )
69+ printf(" cmd: [%s]\n " , cmd)
70+ val (validTest, exitCode, stdout, stderr) = bashCommand(cmd)
71+ stderr.filter { ! _.contains(" Inappropriate ioctl" ) }.foreach { System .err.printf(" stderr [%s]\n " , _) }
72+ stdout.mkString(" \n " )
73+
74+
75+ class BashScriptsTests :
76+ import BashScriptsTests .*
77+ // classpath tests managed by scripting.ClasspathTests.scala
78+
79+ // //////////////////////// begin tests //////////////////////
80+
81+ /* verify that `dist/bin/scala` correctly passes args to the jvm via -J-D for script envtest.sc */
82+ @ Test def verifyScJProperty =
83+ val tag = " World1"
84+ val stdout = callScript(tag, envtestSc, s " -J-Dkey " )
85+ assertEquals( s " Hello $tag" , stdout)
86+
87+ /* verify that `dist/bin/scala` correctly passes args to the jvm via -J-D for script envtest.scala */
88+ @ Test def verifyScalaJProperty =
89+ val tag = " World2"
90+ val stdout = callScript(tag, envtestScala, s " -J-Dkey " )
91+ assertEquals(s " Hello $tag" , stdout)
92+
93+ /* verify that `dist/bin/scala` can set system properties via -D for envtest.sc */
94+ @ Test def verifyScDProperty =
95+ val tag = " World3"
96+ val stdout = callScript(tag, envtestSc, s " -Dkey " )
97+ assertEquals(s " Hello $tag" , stdout)
98+
99+ /* verify that `dist/bin/scala` can set system properties via -D for envtest.scala */
100+ @ Test def verifyScalaDProperty =
101+ val tag = " World4"
102+ val stdout = callScript(tag, envtestScala, s " -Dkey " )
103+ assertEquals(s " Hello $tag" , stdout)
104+
105+ /* verify that `dist/bin/scala` can set system properties via -D when executing compiled script via -jar envtest.jar */
106+ @ Test def saveAndRunWithDProperty =
107+ val commandline = Seq (" SCALA_OPTS= " , scalaPath.relpath, " -save" , envtestScala.relpath).mkString(" " )
108+ val (_, _, _, _) = bashCommand(commandline) // compile jar, discard output
109+ val testJar = testFile(" envtest.jar" ) // jar is created by the previous bashCommand()
110+ if (testJar.isFile){
111+ printf(" compiled envtest.scala to %s\n " , testJar.norm)
112+ } else {
113+ sys.error(s " error: unable to compile envtest.scala to ${testJar.norm}" )
114+ }
115+
116+ val tag = " World5"
117+ val commandline2 = Seq (" SCALA_OPTS= " , scalaPath.relpath, s " -Dkey= $tag" , testJar.relpath)
118+ printf(" cmd[%s]\n " , commandline2.mkString(" " ))
119+ val (validTest, exitCode, stdout, stderr) = bashCommand(commandline2.mkString(" " ))
120+ assertEquals(s " Hello $tag" , stdout.mkString(" /n" ))
121+
45122 /* verify `dist/bin/scalac` non-interference with command line args following script name */
46123 @ Test def verifyScalacArgs =
47- val commandline = (Seq (scalacPath, " -script" , showArgsScript) ++ testScriptArgs).mkString(" " )
124+ val commandline = (Seq (" SCALA_OPTS= " , scalacPath, " -script" , showArgsScript) ++ testScriptArgs).mkString(" " )
48125 val (validTest, exitCode, stdout, stderr) = bashCommand(commandline)
49- if validTest then
126+ if verifyValid( validTest) then
50127 var fail = false
51128 printf(" \n " )
52129 for (line, expect) <- stdout zip expectedOutput do
@@ -57,46 +134,13 @@ class BashScriptsTests:
57134 if fail then
58135 assert(stdout == expectedOutput)
59136
60- /* verify `dist/bin/scala` with -J setting */
61- @ Test def verifyScJProperty =
62- val commandline = Seq (scalaPath, " -J-Dkey=World" , testFiles.find(_.getName == " envtest.sc" ).get.absPath).mkString(" " )
63- val (validTest, exitCode, stdout, stderr) = bashCommand(commandline)
64- assertEquals(stdout.mkString(" /n" ), " Hello World" )
65-
66- /* verify `dist/bin/scala` with -J setting */
67- @ Test def verifyScalaJProperty =
68- val commandline = Seq (scalaPath, " -J-Dkey=World3" , testFiles.find(_.getName == " envtest.scala" ).get.absPath).mkString(" " )
69- val (validTest, exitCode, stdout, stderr) = bashCommand(commandline)
70- assertEquals(stdout.mkString(" /n" ), " Hello World3" )
71-
72- /* verify `dist/bin/scala` with -D setting */
73- @ Test def verifyScDProperty =
74- val commandline = Seq (scalaPath, " -Dkey=World3" , testFiles.find(_.getName == " envtest.sc" ).get.absPath).mkString(" " )
75- val (validTest, exitCode, stdout, stderr) = bashCommand(commandline)
76- assertEquals(stdout.mkString(" /n" ), " Hello World3" )
77-
78- /* verify `dist/bin/scala` with -D setting */
79- @ Test def verifyScalaDProperty =
80- val commandline = Seq (scalaPath, " -Dkey=World4" , testFiles.find(_.getName == " envtest.scala" ).get.absPath).mkString(" " )
81- val (validTest, exitCode, stdout, stderr) = bashCommand(commandline)
82- assertEquals(stdout.mkString(" /n" ), " Hello World4" )
83-
84- /* verify `dist/bin/scala` with -D setting */
85- @ Test def saveAndRunWithDProperty =
86- val commandline = Seq (scalaPath, " -save" , testFiles.find(_.getName == " envtest.scala" ).get.absPath).mkString(" " )
87- val (_, _, _, _) = bashCommand(commandline)
88- val commandline2 = Seq (scalaPath, " -Dkey=World5" , testFiles.find(_.getName == " envtest.jar" ).get.absPath).mkString(" " )
89- val (validTest, exitCode, stdout, stderr) = bashCommand(commandline2)
90- assertEquals(stdout.mkString(" /n" ), " Hello World5" )
91-
92137 /* verify `dist/bin/scala` non-interference with command line args following script name */
93138 @ Test def verifyScalaArgs =
94139 val commandline = (Seq (" SCALA_OPTS= " , scalaPath, showArgsScript) ++ testScriptArgs).mkString(" " )
95140 val (validTest, exitCode, stdout, stderr) = bashCommand(commandline)
96- if validTest then
141+ if verifyValid( validTest) then
97142 var fail = false
98143 printf(" \n " )
99- var mismatches = List .empty[(String , String )]
100144 for (line, expect) <- stdout zip expectedOutput do
101145 printf(" expected: %-17s\n actual : %s\n " , expect, line)
102146 if line != expect then
@@ -115,7 +159,7 @@ class BashScriptsTests:
115159 printf(" ===> verify valid system property script.path is reported by script [%s]\n " , scriptFile.getName)
116160 printf(" calling scriptFile: %s\n " , scriptFile)
117161 val (validTest, exitCode, stdout, stderr) = bashCommand(scriptFile.absPath)
118- if validTest then
162+ if verifyValid( validTest) then
119163 stdout.foreach { printf(" stdout: [%s]\n " , _) }
120164 stderr.foreach { printf(" stderr: [%s]\n " , _) }
121165 val valid = stdout.exists { _.endsWith(expected) }
@@ -131,113 +175,16 @@ class BashScriptsTests:
131175 val envPairs = List ((" SCALA_OPTS" , s " @ $argsfile" ))
132176 val (validTest, exitCode, stdout, stderr) = bashCommand(scriptFile.absPath, envPairs)
133177 printf(" stdout: %s\n " , stdout.mkString(" \n " ," \n " ," " ))
134- if validTest then
178+ if verifyValid( validTest) then
135179 val expected = s " ${workingDirectory.norm}"
136- val output = stdout.find( _.trim.startsWith(" cwd" ) ).getOrElse(" " ).dropWhile(_!= ' ' ).trim
137- printf(" output [%s]\n " , output)
180+ // stdout might be polluted with an ANSI color prefix, so be careful
181+ val cwdline = stdout.find( _.trim.matches(" .*cwd: .*" ) ).getOrElse(" " )
182+ printf(" cwdline [%s]\n " , cwdline)
138183 printf(" expected[%s]\n " , expected)
139- val valid = output.startsWith(expected)
184+ val valid = cwdline.endsWith(expected)
185+ if (! valid) then
186+ stdout.foreach { printf(" stdout[%s]\n " , _) }
187+ stderr.foreach { printf(" stderr[%s]\n " , _) }
140188 if valid then printf(s " \n ===> success: classpath begins with %s, as reported by [%s] \n " , workingDirectory, scriptFile.getName)
141189 assert(valid, s " script ${scriptFile.absPath} did not report valid java.class.path first entry " )
142190
143- def existingPath : String = envOrElse(" PATH" , " " ).norm
144- def adjustedPath = s " $javaHome/bin $psep$scalaHome/bin $psep$existingPath"
145- def pathEntries = adjustedPath.split(psep).toList
146-
147- lazy val argsfile = createArgsFile() // avoid problems caused by drive letter
148- def createArgsFile (): String =
149- val utfCharset = java.nio.charset.StandardCharsets .UTF_8 .name
150- val path = Files .createTempFile(" scriptingTest" , " .args" )
151- val text = s " -classpath ${workingDirectory.absPath}"
152- Files .write(path, text.getBytes(utfCharset))
153- path.toFile.getAbsolutePath.norm
154-
155- def fixHome (s : String ): String =
156- s.startsWith(" ~" ) match {
157- case false => s
158- case true => s.replaceFirst(" ~" , userHome)
159- }
160-
161- extension(s : String ) {
162- def toPath : Path = Paths .get(fixHome(s)) // .toAbsolutePath
163- def toFile : File = s.toPath.toFile
164- def absPath : String = s.toFile.absPath
165- def norm : String = s.replace('\\ ' , '/' ) // bash expects forward slash
166- def isFile : Boolean = s.toFile.isFile
167- def exists : Boolean = s.toPath.toFile.exists
168- def name : String = s.toFile.getName
169- def dropExtension : String = s.reverse.dropWhile(_ != '.' ).drop(1 ).reverse
170- def parent (up : Int ): String = s.norm.split(" /" ).reverse.drop(up).reverse.mkString(" /" )
171- }
172-
173- extension(p : Path ) {
174- def listFiles : Seq [File ] = p.toFile.listFiles.toList
175- def norm : String = p.normalize.toString.replace('\\ ' , '/' )
176- def name : String = p.toFile.getName
177- }
178-
179- extension(f : File ) {
180- def name = f.getName
181- def norm : String = f.toPath.normalize.norm
182- def absPath : String = f.getAbsolutePath.norm
183- }
184-
185- lazy val psep : String = propOrElse(" path.separator" , " " )
186- lazy val osname = propOrElse(" os.name" , " " ).toLowerCase
187-
188- lazy val scalacPath = s " $workingDirectory/dist/target/pack/bin/scalac " .norm
189- lazy val scalaPath = s " $workingDirectory/dist/target/pack/bin/scala " .norm
190-
191- // use optional working directory TEST_CWD, if defined
192- lazy val workingDirectory : String = envOrElse(" TEST_CWD" , userDir)
193-
194- // use optional TEST_BASH if defined, otherwise, bash must be in PATH
195- lazy val bashExe : String = envOrElse(" TEST_BASH" , whichBash)
196-
197- // test env SCALA_HOME is:
198- // dist/target/pack, if present
199- // else, SCALA_HOME if defined
200- // else, not defined
201- lazy val scalaHome =
202- if scalacPath.isFile then scalacPath.replaceAll(" /bin/scalac" , " " )
203- else envOrElse(" SCALA_HOME" , " " ).norm
204-
205- lazy val javaHome = whichJava.parent(2 )
206-
207- lazy val testEnvPairs = List (
208- (" JAVA_HOME" , javaHome),
209- (" SCALA_HOME" , scalaHome),
210- (" PATH" , adjustedPath),
211- ).filter { case (name, valu) => valu.nonEmpty }
212-
213- lazy val whichBash : String = whichExe(" bash" )
214- lazy val whichJava : String = whichExe(" java" )
215-
216- def whichExe (basename : String ): String =
217- val exeName = if (osname.toLowerCase.startsWith(" windows" )) s " $basename.exe " else basename
218- which(exeName)
219-
220- def bashCommand (cmdstr : String , additionalEnvPairs : List [(String , String )] = Nil ): (Boolean , Int , Seq [String ], Seq [String ]) = {
221- var (stdout, stderr) = (List .empty[String ], List .empty[String ])
222- if bashExe.toFile.exists then
223- val cmd = Seq (bashExe, " -c" , cmdstr)
224- val envPairs = testEnvPairs ++ additionalEnvPairs
225- val proc = Process (cmd, None , envPairs * )
226- val exitVal = proc ! ProcessLogger (
227- (out : String ) => stdout ::= out,
228- (err : String ) => stderr ::= err
229- )
230- val validTest = exitVal == 0 && ! stderr.exists(_.contains(" Permission denied" ))
231- if ! validTest then
232- printf(" \n unable to execute script, return value is %d\n " , exitVal)
233- stderr.foreach { System .err.printf(" stderr [%s]\n " , _) }
234- (validTest, exitVal, stdout.reverse, stderr.reverse)
235- else
236- (false , - 1 , Nil , Nil )
237- }
238-
239- def execCmd (command : String , options : String * ): Seq [String ] =
240- val cmd = (command :: options.toList).toSeq
241- for {
242- line <- Process (cmd).lazyLines_!
243- } yield line
0 commit comments