Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NIT Refactor existing --watch tests #3175

Merged
merged 4 commits into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading