Skip to content

Commit

Permalink
Merge pull request #3175 from Gedochao/maintenance/watch-using-direct…
Browse files Browse the repository at this point in the history
…ives

NIT Refactor existing `--watch` tests
  • Loading branch information
Gedochao authored Sep 15, 2024
2 parents a708ae9 + c30f908 commit 7123556
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 279 deletions.
61 changes: 29 additions & 32 deletions modules/build/src/main/scala/scala/build/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,7 @@ object Build {
testDocOpt: Option[Build]
)

def doBuild(
overrideOptions: BuildOptions
): Either[BuildException, NonCrossBuilds] = either {
def doBuild(overrideOptions: BuildOptions): Either[BuildException, NonCrossBuilds] = either {

val inputs0 = updateInputs(
inputs,
Expand Down Expand Up @@ -685,9 +683,9 @@ object Build {
val threads = BuildThreads.create()
val classesDir0 = classesRootDir(inputs.workspace, inputs.projectName)
val info = either {
val (crossSources, inputs0) = value(allInputs(inputs, options, logger))
val sharedOptions = crossSources.sharedOptions(options)
val compiler = value {
val (crossSources: CrossSources, inputs0: Inputs) = value(allInputs(inputs, options, logger))
val sharedOptions = crossSources.sharedOptions(options)
val compiler: ScalaCompiler = value {
compilerMaker.create(
inputs0.workspace / Constants.workspaceDirName,
classesDir0,
Expand All @@ -696,7 +694,7 @@ object Build {
sharedOptions
)
}
val docCompilerOpt = docCompilerMakerOpt.map(_.create(
val docCompilerOpt: Option[ScalaCompiler] = docCompilerMakerOpt.map(_.create(
inputs0.workspace / Constants.workspaceDirName,
classesDir0,
buildClient,
Expand All @@ -711,20 +709,26 @@ object Build {
def run(): Unit = {
try {
res =
info.flatMap { case (compiler, docCompilerOpt, crossSources, inputs) =>
build(
inputs,
crossSources,
options,
logger,
buildClient,
compiler,
docCompilerOpt,
crossBuilds = crossBuilds,
buildTests = buildTests,
partial = partial,
actionableDiagnostics = actionableDiagnostics
)
info.flatMap {
case (
compiler: ScalaCompiler,
docCompilerOpt: Option[ScalaCompiler],
crossSources: CrossSources,
inputs: Inputs
) =>
build(
inputs = inputs,
crossSources = crossSources,
options = options,
logger = logger,
buildClient = buildClient,
compiler = compiler,
docCompilerOpt = docCompilerOpt,
crossBuilds = crossBuilds,
buildTests = buildTests,
partial = partial,
actionableDiagnostics = actionableDiagnostics
)
}
action(res)
}
Expand All @@ -742,7 +746,7 @@ object Build {

def doWatch(): Unit = {
val elements: Seq[Element] =
if (res == null) inputs.elements
if res == null then inputs.elements
else
res
.map { builds =>
Expand Down Expand Up @@ -777,10 +781,7 @@ object Build {
case _: Virtual =>
}
watcher0.addObserver {
onChangeBufferedObserver { event =>
if (eventFilter(event))
watcher.schedule()
}
onChangeBufferedObserver(event => if eventFilter(event) then watcher.schedule())
}
}

Expand All @@ -797,14 +798,10 @@ object Build {
}
.getOrElse(Nil)
for (artifact <- artifacts) {
val depth = if (os.isFile(artifact)) -1 else Int.MaxValue
val depth = if os.isFile(artifact) then -1 else Int.MaxValue
val watcher0 = watcher.newWatcher()
watcher0.register(artifact.toNIO, depth)
watcher0.addObserver {
onChangeBufferedObserver { _ =>
watcher.schedule()
}
}
watcher0.addObserver(onChangeBufferedObserver(_ => watcher.schedule()))
}
}

Expand Down
24 changes: 10 additions & 14 deletions modules/cli/src/main/scala/scala/cli/commands/run/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -223,16 +223,15 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
Some(name),
inputArgs
)
if (CommandUtils.shouldCheckUpdate)
Update.checkUpdateSafe(logger)
if CommandUtils.shouldCheckUpdate then Update.checkUpdateSafe(logger)

val configDb = ConfigDbUtils.configDb.orExit(logger)
val actionableDiagnostics =
options.shared.logging.verbosityOptions.actions.orElse(
configDb.get(Keys.actions).getOrElse(None)
)

if (options.sharedRun.watch.watchMode) {
if options.sharedRun.watch.watchMode then {

/** A handle to the Runner process, used to kill the process if it's still alive when a change
* occured and restarts are allowed or to wait for it if restarts are not allowed
Expand All @@ -251,20 +250,18 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
val mainThreadOpt = AtomicReference(Option.empty[Thread])

val watcher = Build.watch(
inputs,
initialBuildOptions,
compilerMaker,
None,
logger,
inputs = inputs,
options = initialBuildOptions,
compilerMaker = compilerMaker,
docCompilerMakerOpt = None,
logger = logger,
crossBuilds = cross,
buildTests = false,
partial = None,
actionableDiagnostics = actionableDiagnostics,
postAction = () =>
if (processOpt.get().exists(_._1.isAlive()))
WatchUtil.printWatchWhileRunningMessage()
else
WatchUtil.printWatchMessage()
if processOpt.get().exists(_._1.isAlive()) then WatchUtil.printWatchWhileRunningMessage()
else WatchUtil.printWatchMessage()
) { res =>
for ((process, onExitProcess) <- processOpt.get()) {
onExitProcess.cancel(true)
Expand Down Expand Up @@ -296,8 +293,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
(proc, onExit)
}
s.copyOutput(options.shared)
if (options.sharedRun.watch.restart)
processOpt.set(maybeProcess)
if options.sharedRun.watch.restart then processOpt.set(maybeProcess)
else {
for ((proc, onExit) <- maybeProcess)
ProcUtil.waitForProcess(proc, onExit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import java.io.{ByteArrayOutputStream, File}
import java.nio.charset.Charset

import scala.cli.integration.util.DockerServer
import scala.concurrent.ExecutionContext
import scala.concurrent.duration.Duration
import scala.io.Codec
import scala.jdk.CollectionConverters.*
import scala.util.Properties
Expand Down Expand Up @@ -1507,99 +1505,6 @@ abstract class RunTestDefinitions
}
}

test("watch with interactive, with multiple main classes") {
val fileName = "watch.scala"

val inputs = TestInputs(
os.rel / fileName ->
"""object Run1 extends App {println("Run1 launched")}
|object Run2 extends App {println("Run2 launched")}
|""".stripMargin
)
inputs.fromRoot { root =>
val confDir = root / "config"
val confFile = confDir / "test-config.json"

os.write(confFile, "{}", createFolders = true)

if (!Properties.isWin)
os.perms.set(confDir, "rwx------")

val configEnv = Map("SCALA_CLI_CONFIG" -> confFile.toString)

val proc = os.proc(TestUtil.cli, "run", "--watch", "--interactive", fileName)
.spawn(
cwd = root,
mergeErrIntoOut = true,
stdout = os.Pipe,
stdin = os.Pipe,
env = Map("SCALA_CLI_INTERACTIVE" -> "true") ++ configEnv
)

try
TestUtil.withThreadPool("run-watch-interactive-multi-main-class-test", 2) { pool =>
val timeout = Duration("60 seconds")
implicit val ec = ExecutionContext.fromExecutorService(pool)

def lineReaderIter = Iterator.continually {
val line = TestUtil.readLine(proc.stdout, ec, timeout)
println(s"Line read: $line")
line
}

def checkLinesForError(lines: Seq[String]) = munit.Assertions.assert(
!lines.exists { line =>
TestUtil.removeAnsiColors(line).contains("[error]")
},
clues(lines.toSeq)
)

def answerInteractivePrompt(id: Int) = {
val interactivePromptLines = lineReaderIter
.takeWhile(!_.startsWith("[1]" /* probably [1] Run2 or [1] No*/ ))
.toList
expect(interactivePromptLines.nonEmpty)
checkLinesForError(interactivePromptLines)
proc.stdin.write(s"$id\n")
proc.stdin.flush()
}

def analyzeRunOutput(restart: Boolean) = {
val runResultLines = lineReaderIter
.takeWhile(!_.contains("press Enter to re-run"))
.toList
expect(runResultLines.nonEmpty)
checkLinesForError(runResultLines)
if (restart)
proc.stdin.write("\n")
proc.stdin.flush()
}

// You have run the current scala-cli command with the --interactive mode turned on.
// Would you like to leave it on permanently?
answerInteractivePrompt(0)

// Found several main classes. Which would you like to run?
answerInteractivePrompt(0)
expect(TestUtil.readLine(proc.stdout, ec, timeout) == "Run1 launched")

analyzeRunOutput( /* restart */ true)

answerInteractivePrompt(1)
expect(TestUtil.readLine(proc.stdout, ec, timeout) == "Run2 launched")

analyzeRunOutput( /* restart */ false)
}
finally
if (proc.isAlive()) {
proc.destroy()
Thread.sleep(200L)
if (proc.isAlive())
proc.destroyForcibly()
}
}
}

test("decoded classNames in interactive ask") {
val fileName = "watch.scala"

Expand Down
Loading

0 comments on commit 7123556

Please sign in to comment.