From 66f4032699d69f6b854d43fe0de92a190ede78da Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Thu, 22 Oct 2020 11:09:00 +0200 Subject: [PATCH 1/3] Fix BuildServerReporter and add tests --- .../internal/server/BuildServerReporter.scala | 16 +++--- .../src/server-test/buildserver/build.sbt | 12 +++-- .../foo/src/test/scala/foo/FooTest.scala | 9 ---- .../src/main/scala/reporterror/Error.scala | 5 ++ .../main/scala/reportwarning/Warning.scala | 7 +++ .../src/main/scala/main/Main.scala} | 4 +- .../src/test/scala/tests}/FailingTest.scala | 2 +- .../src/test/scala/tests/PassingTest.scala | 9 ++++ .../test/scala/testpkg/BuildServerTest.scala | 49 +++++++++++++++---- 9 files changed, 80 insertions(+), 33 deletions(-) delete mode 100644 server-test/src/server-test/buildserver/foo/src/test/scala/foo/FooTest.scala create mode 100644 server-test/src/server-test/buildserver/report-error/src/main/scala/reporterror/Error.scala create mode 100644 server-test/src/server-test/buildserver/report-warning/src/main/scala/reportwarning/Warning.scala rename server-test/src/server-test/buildserver/{foo/src/main/scala/foo/FooMain.scala => run-and-test/src/main/scala/main/Main.scala} (59%) rename server-test/src/server-test/buildserver/{foo/src/test/scala/foo => run-and-test/src/test/scala/tests}/FailingTest.scala (90%) create mode 100644 server-test/src/server-test/buildserver/run-and-test/src/test/scala/tests/PassingTest.scala diff --git a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala index c6c95106c..164f03832 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala @@ -7,6 +7,8 @@ package sbt.internal.server +import java.nio.file.Path + import sbt.StandardMain import sbt.internal.bsp._ import sbt.internal.util.ManagedLogger @@ -69,7 +71,7 @@ final class BuildServerReporterImpl( import sbt.internal.inc.JavaInterfaceUtil._ private lazy val exchange = StandardMain.exchange - private val problemsByFile = mutable.Map[VirtualFileRef, Vector[Diagnostic]]() + private val problemsByFile = mutable.Map[Path, Vector[Diagnostic]]() override def sendSuccessReport(analysis: CompileAnalysis): Unit = { for { @@ -90,9 +92,8 @@ final class BuildServerReporterImpl( override def sendFailureReport(sources: Array[VirtualFile]): Unit = { for (source <- sources) { - val ref = VirtualFileRef.of(source.id()) - val diagnostics = problemsByFile.getOrElse(ref, Vector()) val filePath = converter.toPath(source) + val diagnostics = problemsByFile.getOrElse(filePath, Vector()) val params = PublishDiagnosticsParams( textDocument = TextDocumentIdentifier(filePath.toUri), buildTarget, @@ -106,14 +107,13 @@ final class BuildServerReporterImpl( protected override def publishDiagnostic(problem: Problem): Unit = { for { - path <- problem.position().sourcePath.toOption - source <- problem.position.sourceFile.toOption + id <- problem.position.sourcePath.toOption diagnostic <- toDiagnostic(problem) } { - val fileId = VirtualFileRef.of(path) - problemsByFile(fileId) = problemsByFile.getOrElse(fileId, Vector()) :+ diagnostic + val filePath = converter.toPath(VirtualFileRef.of(id)) + problemsByFile(filePath) = problemsByFile.getOrElse(filePath, Vector()) :+ diagnostic val params = PublishDiagnosticsParams( - TextDocumentIdentifier(source.toURI), + TextDocumentIdentifier(filePath.toUri), buildTarget, originId = None, Vector(diagnostic), diff --git a/server-test/src/server-test/buildserver/build.sbt b/server-test/src/server-test/buildserver/build.sbt index fbbfc556b..e1a151256 100644 --- a/server-test/src/server-test/buildserver/build.sbt +++ b/server-test/src/server-test/buildserver/build.sbt @@ -2,13 +2,17 @@ ThisBuild / scalaVersion := "2.13.1" Global / serverLog / logLevel := Level.Debug -lazy val root = (project in file(".")) - .aggregate(foo, util) - -lazy val foo = project.in(file("foo")) +lazy val runAndTest = project.in(file("run-and-test")) .settings( libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % "test", ) .dependsOn(util) +lazy val reportError = project.in(file("report-error")) + +lazy val reportWarning = project.in(file("report-warning")) + .settings( + scalacOptions += "-deprecation" + ) + lazy val util = project diff --git a/server-test/src/server-test/buildserver/foo/src/test/scala/foo/FooTest.scala b/server-test/src/server-test/buildserver/foo/src/test/scala/foo/FooTest.scala deleted file mode 100644 index 1874da8c1..000000000 --- a/server-test/src/server-test/buildserver/foo/src/test/scala/foo/FooTest.scala +++ /dev/null @@ -1,9 +0,0 @@ -package foo - -import org.scalatest.FreeSpec - -class FooTest extends FreeSpec { - "test message" in { - assert(FooMain.message == "Hello World!") - } -} \ No newline at end of file diff --git a/server-test/src/server-test/buildserver/report-error/src/main/scala/reporterror/Error.scala b/server-test/src/server-test/buildserver/report-error/src/main/scala/reporterror/Error.scala new file mode 100644 index 000000000..496e48b8e --- /dev/null +++ b/server-test/src/server-test/buildserver/report-error/src/main/scala/reporterror/Error.scala @@ -0,0 +1,5 @@ +package reportertests + +object Error { + val version: String = 5 +} diff --git a/server-test/src/server-test/buildserver/report-warning/src/main/scala/reportwarning/Warning.scala b/server-test/src/server-test/buildserver/report-warning/src/main/scala/reportwarning/Warning.scala new file mode 100644 index 000000000..6a9554a70 --- /dev/null +++ b/server-test/src/server-test/buildserver/report-warning/src/main/scala/reportwarning/Warning.scala @@ -0,0 +1,7 @@ +package reportertests + +object Warning { + def print() { + prtinln("bar") + } +} diff --git a/server-test/src/server-test/buildserver/foo/src/main/scala/foo/FooMain.scala b/server-test/src/server-test/buildserver/run-and-test/src/main/scala/main/Main.scala similarity index 59% rename from server-test/src/server-test/buildserver/foo/src/main/scala/foo/FooMain.scala rename to server-test/src/server-test/buildserver/run-and-test/src/main/scala/main/Main.scala index 348be2497..8ccc2eb6c 100644 --- a/server-test/src/server-test/buildserver/foo/src/main/scala/foo/FooMain.scala +++ b/server-test/src/server-test/buildserver/run-and-test/src/main/scala/main/Main.scala @@ -1,6 +1,6 @@ -package foo +package main -object FooMain extends App { +object Main extends App { lazy val message = "Hello World!" println(message) diff --git a/server-test/src/server-test/buildserver/foo/src/test/scala/foo/FailingTest.scala b/server-test/src/server-test/buildserver/run-and-test/src/test/scala/tests/FailingTest.scala similarity index 90% rename from server-test/src/server-test/buildserver/foo/src/test/scala/foo/FailingTest.scala rename to server-test/src/server-test/buildserver/run-and-test/src/test/scala/tests/FailingTest.scala index 886d3d6aa..ee03bfbb4 100644 --- a/server-test/src/server-test/buildserver/foo/src/test/scala/foo/FailingTest.scala +++ b/server-test/src/server-test/buildserver/run-and-test/src/test/scala/tests/FailingTest.scala @@ -1,4 +1,4 @@ -package foo +package tests import org.scalatest.FreeSpec diff --git a/server-test/src/server-test/buildserver/run-and-test/src/test/scala/tests/PassingTest.scala b/server-test/src/server-test/buildserver/run-and-test/src/test/scala/tests/PassingTest.scala new file mode 100644 index 000000000..9dab63ca9 --- /dev/null +++ b/server-test/src/server-test/buildserver/run-and-test/src/test/scala/tests/PassingTest.scala @@ -0,0 +1,9 @@ +package tests + +import org.scalatest.FreeSpec + +class PassingTest extends FreeSpec { + "test message" in { + assert(main.Main.message == "Hello World!") + } +} \ No newline at end of file diff --git a/server-test/src/test/scala/testpkg/BuildServerTest.scala b/server-test/src/test/scala/testpkg/BuildServerTest.scala index c6d9c75ec..3f0a7acea 100644 --- a/server-test/src/test/scala/testpkg/BuildServerTest.scala +++ b/server-test/src/test/scala/testpkg/BuildServerTest.scala @@ -86,7 +86,7 @@ object BuildServerTest extends AbstractServerTest { } test("buildTarget/scalaMainClasses") { _ => - val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#foo/Compile" + val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#runAndTest/Compile" svr.sendJsonRpc( s"""{ "jsonrpc": "2.0", "id": "16", "method": "buildTarget/scalaMainClasses", "params": { | "targets": [{ "uri": "$x" }] @@ -95,17 +95,17 @@ object BuildServerTest extends AbstractServerTest { assert(svr.waitForString(30.seconds) { s => println(s) (s contains """"id":"16"""") && - (s contains """"class":"foo.FooMain"""") + (s contains """"class":"main.Main"""") }) } test("buildTarget/run") { _ => - val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#foo/Compile" + val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#runAndTest/Compile" svr.sendJsonRpc( s"""{ "jsonrpc": "2.0", "id": "17", "method": "buildTarget/run", "params": { | "target": { "uri": "$x" }, | "dataKind": "scala-main-class", - | "data": { "class": "foo.FooMain" } + | "data": { "class": "main.Main" } |} }""".stripMargin ) assert(svr.waitForString(10.seconds) { s => @@ -121,7 +121,7 @@ object BuildServerTest extends AbstractServerTest { } test("buildTarget/scalaTestClasses") { _ => - val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#foo/Test" + val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#runAndTest/Test" svr.sendJsonRpc( s"""{ "jsonrpc": "2.0", "id": "18", "method": "buildTarget/scalaTestClasses", "params": { | "targets": [{ "uri": "$x" }] @@ -130,12 +130,13 @@ object BuildServerTest extends AbstractServerTest { assert(svr.waitForString(10.seconds) { s => println(s) (s contains """"id":"18"""") && - (s contains """"classes":["foo.FailingTest","foo.FooTest"]""") + (s contains """"tests.FailingTest"""") && + (s contains """"tests.PassingTest"""") }) } test("buildTarget/test: run all tests") { _ => - val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#foo/Test" + val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#runAndTest/Test" svr.sendJsonRpc( s"""{ "jsonrpc": "2.0", "id": "19", "method": "buildTarget/test", "params": { | "targets": [{ "uri": "$x" }] @@ -149,7 +150,7 @@ object BuildServerTest extends AbstractServerTest { } test("buildTarget/test: run one test class") { _ => - val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#foo/Test" + val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#runAndTest/Test" svr.sendJsonRpc( s"""{ "jsonrpc": "2.0", "id": "20", "method": "buildTarget/test", "params": { | "targets": [{ "uri": "$x" }], @@ -158,7 +159,7 @@ object BuildServerTest extends AbstractServerTest { | "testClasses": [ | { | "target": { "uri": "$x" }, - | "classes": ["foo.FooTest"] + | "classes": ["tests.PassingTest"] | } | ] | } @@ -171,6 +172,36 @@ object BuildServerTest extends AbstractServerTest { }) } + test("buildTarget/compile: report error") { _ => + val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#reportError/Compile" + svr.sendJsonRpc( + s"""{ "jsonrpc": "2.0", "id": "21", "method": "buildTarget/compile", "params": { + | "targets": [{ "uri": "$x" }] + |} }""".stripMargin + ) + assert(svr.waitForString(10.seconds) { s => + println(s) + (s contains s""""buildTarget":{"uri":"$x"}""") && + (s contains """"severity":1""") && + (s contains """"reset":true""") + }) + } + + test("buildTarget/compile: report warning") { _ => + val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#reportWarning/Compile" + svr.sendJsonRpc( + s"""{ "jsonrpc": "2.0", "id": "22", "method": "buildTarget/compile", "params": { + | "targets": [{ "uri": "$x" }] + |} }""".stripMargin + ) + assert(svr.waitForString(10.seconds) { s => + println(s) + (s contains s""""buildTarget":{"uri":"$x"}""") && + (s contains """"severity":2""") && + (s contains """"reset":true""") + }) + } + def initializeRequest(): Unit = { svr.sendJsonRpc( """{ "jsonrpc": "2.0", "id": "10", "method": "build/initialize", From beab10fc64bc120257f237ce59767899837d1e27 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Sun, 25 Oct 2020 11:53:21 -0700 Subject: [PATCH 2/3] Add wizard for installing sbtn and completions This commit adds a wizard for installing sbtn along with tab completions for bash, fish, powershell and zsh. It introduces the `installSbtn` command which installs sbtn into ~/.sbt/1.0/bin/sbtn(.exe) depending on the platform. It also can optionally install completions. The completions are installed into ~/.sbt/1.0/completions. The sbtn native executable is installed by downloading the sbt universal zip for the version (which can be provided as an input argument with a fallback to the running sbt version) and extracting the platform specific binary into ~/.sbt/1.0/bin. After installing the executable, it offers to setup the path and completions for the four shells. With the user's consent, it adds a line to the shell config that updates the path to include ~/.sbt/1.0/bin and another line to source the appropriate completion file for the shell from ~/.sbt/1.0/completions. --- main/src/main/scala/sbt/Defaults.scala | 2 + .../main/scala/sbt/internal/InstallSbtn.scala | 226 ++++++++++++++++++ .../scala/sbt/internal/TaskProgress.scala | 1 + .../scala/sbt/internal/InstallSbtnSpec.scala | 66 +++++ 4 files changed, 295 insertions(+) create mode 100644 main/src/main/scala/sbt/internal/InstallSbtn.scala create mode 100644 main/src/test/scala/sbt/internal/InstallSbtnSpec.scala diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 46b81844c..2f4b7ebaa 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -391,6 +391,8 @@ object Defaults extends BuildCommon { canonicalInput :== true, echoInput :== true, terminal := state.value.get(terminalKey).getOrElse(Terminal(ITerminal.get)), + InstallSbtn.installSbtn := InstallSbtn.installSbtnImpl.evaluated, + InstallSbtn.installSbtn / aggregate := false, ) ++ LintUnused.lintSettings ++ DefaultBackgroundJobService.backgroundJobServiceSettings ++ RemoteCache.globalSettings diff --git a/main/src/main/scala/sbt/internal/InstallSbtn.scala b/main/src/main/scala/sbt/internal/InstallSbtn.scala new file mode 100644 index 000000000..271a9a0cf --- /dev/null +++ b/main/src/main/scala/sbt/internal/InstallSbtn.scala @@ -0,0 +1,226 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt +package internal + +import Def._ +import Keys.{ sbtVersion, state, terminal } + +import java.io.{ File, FileInputStream, FileOutputStream, InputStream, IOException } +import java.net.URL +import java.nio.file.{ Files, Path } +import java.util.zip.ZipInputStream +import sbt.io.IO +import sbt.io.Path.userHome +import sbt.io.syntax._ +import scala.util.{ Properties, Try } + +private[sbt] object InstallSbtn { + private[sbt] val installSbtn = + Def.inputKey[Unit]("install sbtn and tab completions").withRank(KeyRanks.BTask) + private[sbt] def installSbtnImpl: Def.Initialize[InputTask[Unit]] = Def.inputTask { + val inputVersion = Def.spaceDelimited("version").parsed.headOption + val version = inputVersion.getOrElse(sbtVersion.value.replaceAllLiterally("-SNAPSHOT", "")) + val term = terminal.value + term.setMode(canonical = false, echo = false) + val baseDirectory = BuildPaths.getGlobalBase(state.value).toPath + val tmp = Files.createTempFile(s"sbt-$version", "zip") + val sbtn = if (Properties.isWin) "sbtn.exe" else "sbtn" + try extractSbtn(term, version, tmp, baseDirectory.resolve("bin").resolve(sbtn)) + finally { + Files.deleteIfExists(tmp) + () + } + val shell = if (System.console != null) getShell(term) else "none" + shell match { + case "none" => + case s => + val completion = shellCompletions(s) + val completionLocation = baseDirectory.resolve("completions").resolve(completion) + downloadCompletion(completion, version, completionLocation) + s match { + case "bash" => setupBash(baseDirectory, term) + case "fish" => setupFish(baseDirectory, term) + case "zsh" => setupZsh(baseDirectory, term) + case "powershell" => setupPowershell(baseDirectory, term) + case _ => // should be unreachable + } + val msg = s"Successfully installed sbtn for $s. You may need to restart $s for the " + + "changes to take effect." + term.printStream.println(msg) + } + () + } + + private[sbt] def extractSbtn(term: Terminal, version: String, sbtZip: Path, sbtn: Path): Unit = { + downloadRelease(term, version, sbtZip) + Files.createDirectories(sbtn.getParent) + val bin = + if (Properties.isWin) "pc-win32.exe" + else if (Properties.isLinux) "pc-linux" + else "apple-darwin" + val sbtnName = s"sbt/bin/sbtn-x86_64-$bin" + val fis = new FileInputStream(sbtZip.toFile) + val zipInputStream = new ZipInputStream(fis) + var foundBinary = false + try { + var entry = zipInputStream.getNextEntry + while (entry != null) { + if (entry.getName == sbtnName) { + foundBinary = true + term.printStream.println(s"extracting $sbtZip!$sbtnName to $sbtn") + transfer(zipInputStream, sbtn) + sbtn.toFile.setExecutable(true) + entry = null + } else { + entry = zipInputStream.getNextEntry + } + } + if (!foundBinary) throw new IllegalStateException(s"couldn't find $sbtnName in $sbtZip") + } finally { + fis.close() + zipInputStream.close() + } + () + } + private[this] def downloadRelease(term: Terminal, version: String, location: Path): Unit = { + val zip = s"https://github.com/sbt/sbt/releases/download/v$version/sbt-$version.zip" + val url = new URL(zip) + term.printStream.println(s"downloading $zip to $location") + transfer(url.openStream(), location) + } + private[this] def transfer(inputStream: InputStream, path: Path): Unit = + try { + val os = new FileOutputStream(path.toFile) + try { + val result = new Array[Byte](1024 * 1024) + var bytesRead = -1 + do { + bytesRead = inputStream.read(result) + if (bytesRead > 0) os.write(result, 0, bytesRead) + } while (bytesRead > 0) + } finally os.close() + } finally inputStream.close() + private[this] def getShell(term: Terminal): String = { + term.printStream.print(s"""Setup sbtn for shell: + | [1] bash + | [2] fish + | [3] powershell + | [4] zsh + | [5] none + |Enter option: """.stripMargin) + term.printStream.flush() + val key = term.inputStream.read + term.printStream.println(key.toChar) + key match { + case 49 => "bash" + case 50 => "fish" + case 51 => "powershell" + case 52 => "zsh" + case _ => "none" + } + } + private[this] def downloadCompletion(completion: String, version: String, target: Path): Unit = { + Files.createDirectories(target.getParent) + val comp = s"https://raw.githubusercontent.com/sbt/sbt/v$version/client/completions/$completion" + transfer(new URL(comp).openStream, target) + } + private[this] def setupShell( + shell: String, + baseDirectory: Path, + term: Terminal, + configFile: File, + setPath: Path => String, + setCompletions: Path => String, + ): Unit = { + val bin = baseDirectory.resolve("bin") + val export = setPath(bin) + val completions = baseDirectory.resolve("completions") + val sourceCompletions = setCompletions(completions) + val contents = try IO.read(configFile) + catch { case _: IOException => "" } + if (!contents.contains(export)) { + term.printStream.print(s"Add $bin to PATH in $configFile? y/n (y default): ") + term.printStream.flush() + term.inputStream.read() match { + case 110 => term.printStream.println() + case c => + term.printStream.println(c.toChar) + // put the export at the bottom so that the ~/.sbt/1.0/bin/sbtn is least preferred + // but still on the path + IO.write(configFile, s"$contents\n$export") + } + } + val newContents = try IO.read(configFile) + catch { case _: IOException => "" } + if (!newContents.contains(sourceCompletions)) { + term.printStream.print(s"Add tab completions to $configFile? y/n (y default): ") + term.printStream.flush() + term.inputStream.read() match { + case 110 => + case c => + term.printStream.println(c.toChar) + if (shell == "zsh") { + // delete the .zcompdump file because it can prevent the new completions from + // being recognized + Files.deleteIfExists((userHome / ".zcompdump").toPath) + // put the completions at the top because it is effectively just a source + // so the order in the file doesn't really matter but we want to make sure + // that we set fpath before any autoload command in zsh + IO.write(configFile, s"$sourceCompletions\n$newContents") + } else { + IO.write(configFile, s"$newContents\n$sourceCompletions") + } + } + term.printStream.println() + } + } + private[this] def setupBash(baseDirectory: Path, term: Terminal): Unit = + setupShell( + "bash", + baseDirectory, + term, + userHome / ".bashrc", + bin => s"export PATH=$$PATH:$bin", + completions => s"source $completions/sbtn.bash" + ) + private[this] def setupZsh(baseDirectory: Path, term: Terminal): Unit = { + val comp = (completions: Path) => { + "# The following two lines were added by the sbt installSbtn task:\n" + + s"fpath=($$fpath $completions)\nautoload -Uz compinit; compinit" + } + setupShell("zsh", baseDirectory, term, userHome / ".zshrc", bin => s"path=($$path $bin)", comp) + } + private[this] def setupFish(baseDirectory: Path, term: Terminal): Unit = { + val comp = (completions: Path) => s"source $completions/sbtn.fish" + val path = (bin: Path) => s"set PATH $$PATH $bin" + val config = userHome / ".config" / "fish" / "config.fish" + setupShell("fish", baseDirectory, term, config, path, comp) + } + private[this] def setupPowershell(baseDirectory: Path, term: Terminal): Unit = { + val comp = (completions: Path) => s""". "$completions\\sbtn.ps1"""" + val path = (bin: Path) => s"""$$env:Path += ";$bin"""" + import scala.sys.process._ + Try(Seq("pwsh", "-Command", "echo $PROFILE").!!).foreach { output => + output.linesIterator.toSeq.headOption.foreach { l => + setupShell("pwsh", baseDirectory, term, new File(l), path, comp) + } + } + Try(Seq("powershell", "-Command", "echo $PROFILE").!!).foreach { output => + output.linesIterator.toSeq.headOption.foreach { l => + setupShell("pwsh", baseDirectory, term, new File(l), path, comp) + } + } + } + private[this] val shellCompletions = Map( + "bash" -> "sbtn.bash", + "fish" -> "sbtn.fish", + "powershell" -> "sbtn.ps1", + "zsh" -> "_sbtn", + ) +} diff --git a/main/src/main/scala/sbt/internal/TaskProgress.scala b/main/src/main/scala/sbt/internal/TaskProgress.scala index f8308c5ef..cf9ce2e88 100644 --- a/main/src/main/scala/sbt/internal/TaskProgress.scala +++ b/main/src/main/scala/sbt/internal/TaskProgress.scala @@ -139,6 +139,7 @@ private[sbt] class TaskProgress( } private[this] val skipReportTasks = Set( + "installSbtn", "run", "runMain", "bgRun", diff --git a/main/src/test/scala/sbt/internal/InstallSbtnSpec.scala b/main/src/test/scala/sbt/internal/InstallSbtnSpec.scala new file mode 100644 index 000000000..74c78858c --- /dev/null +++ b/main/src/test/scala/sbt/internal/InstallSbtnSpec.scala @@ -0,0 +1,66 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt +package internal + +import java.io.{ InputStream, OutputStream, PrintStream } +import java.lang.ProcessBuilder +import java.lang.ProcessBuilder.Redirect +import java.nio.file.{ Files, Path } +import java.util.concurrent.TimeUnit +import org.scalatest.FlatSpec +import sbt.io.IO + +class InstallSbtnSpec extends FlatSpec { + private def withTemp[R](ext: String)(f: Path => R): R = { + val tmp = Files.createTempFile("sbt-1.4.1-", ext) + try f(tmp) + finally { + Files.deleteIfExists(tmp) + () + } + } + private[this] val term = new Terminal { + def getHeight: Int = 0 + def getWidth: Int = 0 + def inputStream: InputStream = () => -1 + def printStream: PrintStream = new PrintStream((_ => {}): OutputStream) + def setMode(canonical: Boolean, echo: Boolean): Unit = {} + + } + "InstallSbtn" should "extract native sbtn" in + withTemp(".zip") { tmp => + withTemp(".exe") { sbtn => + InstallSbtn.extractSbtn(term, "1.4.1", tmp, sbtn) + val tmpDir = Files.createTempDirectory("sbtn-test").toRealPath() + Files.createDirectories(tmpDir.resolve("project")) + val foo = tmpDir.resolve("foo") + val fooPath = foo.toString.replaceAllLiterally("\\", "\\\\") + val build = s"""TaskKey[Unit]("foo") := IO.write(file("$fooPath"), "foo")""" + IO.write(tmpDir.resolve("build.sbt").toFile, build) + IO.write( + tmpDir.resolve("project").resolve("build.properties").toFile, + "sbt.version=1.4.1" + ) + try { + val proc = + new ProcessBuilder(sbtn.toString, "foo;shutdown") + .redirectInput(Redirect.INHERIT) + .redirectOutput(Redirect.INHERIT) + .redirectError(Redirect.INHERIT) + .directory(tmpDir.toFile) + .start() + proc.waitFor(1, TimeUnit.MINUTES) + assert(proc.exitValue == 0) + assert(IO.read(foo.toFile) == "foo") + } finally { + sbt.io.IO.delete(tmpDir.toFile) + } + } + } +} From 37e4dc5318d5dc9adf1de1efe8a874630e63791f Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Sun, 25 Oct 2020 20:05:09 -0700 Subject: [PATCH 3/3] Disable InstallSbtnSpec This test works fine locally on all platforms but there are issues in CI. I think that it might work ok with 1.4.2 without a lot of extra effort so I'm going to disable it for now. --- main/src/test/scala/sbt/internal/InstallSbtnSpec.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/src/test/scala/sbt/internal/InstallSbtnSpec.scala b/main/src/test/scala/sbt/internal/InstallSbtnSpec.scala index 74c78858c..3580007ed 100644 --- a/main/src/test/scala/sbt/internal/InstallSbtnSpec.scala +++ b/main/src/test/scala/sbt/internal/InstallSbtnSpec.scala @@ -33,7 +33,8 @@ class InstallSbtnSpec extends FlatSpec { def setMode(canonical: Boolean, echo: Boolean): Unit = {} } - "InstallSbtn" should "extract native sbtn" in + // This test has issues in ci but runs ok locally on all platforms + "InstallSbtn" should "extract native sbtn" ignore withTemp(".zip") { tmp => withTemp(".exe") { sbtn => InstallSbtn.extractSbtn(term, "1.4.1", tmp, sbtn)