mirror of https://github.com/sbt/sbt.git
Merge pull request #7417 from adpi2/build-server-source-mapping
[1.10.x] Use sourcePositionMappers to report BSP diagnostics
This commit is contained in:
commit
3e205d04b8
|
|
@ -33,7 +33,6 @@ import sjsonnew.shaded.scalajson.ast.unsafe.{ JNull, JValue }
|
||||||
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter, Parser => JsonParser }
|
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter, Parser => JsonParser }
|
||||||
import xsbti.CompileFailed
|
import xsbti.CompileFailed
|
||||||
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
@ -44,6 +43,8 @@ import scala.util.{ Failure, Success, Try }
|
||||||
import scala.annotation.nowarn
|
import scala.annotation.nowarn
|
||||||
import sbt.testing.Framework
|
import sbt.testing.Framework
|
||||||
import scala.collection.immutable.ListSet
|
import scala.collection.immutable.ListSet
|
||||||
|
import xsbti.VirtualFileRef
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
object BuildServerProtocol {
|
object BuildServerProtocol {
|
||||||
import sbt.internal.bsp.codec.JsonProtocol._
|
import sbt.internal.bsp.codec.JsonProtocol._
|
||||||
|
|
@ -329,11 +330,13 @@ object BuildServerProtocol {
|
||||||
val underlying = (Keys.compile / compilerReporter).value
|
val underlying = (Keys.compile / compilerReporter).value
|
||||||
val logger = streams.value.log
|
val logger = streams.value.log
|
||||||
val meta = isMetaBuild.value
|
val meta = isMetaBuild.value
|
||||||
|
val spms = sourcePositionMappers.value
|
||||||
if (bspEnabled.value) {
|
if (bspEnabled.value) {
|
||||||
new BuildServerReporterImpl(
|
new BuildServerReporterImpl(
|
||||||
targetId,
|
targetId,
|
||||||
bspCompileStateInstance,
|
bspCompileStateInstance,
|
||||||
converter,
|
converter,
|
||||||
|
Defaults.foldMappers(spms, reportAbsolutePath.value, fileConverter.value),
|
||||||
meta,
|
meta,
|
||||||
logger,
|
logger,
|
||||||
underlying
|
underlying
|
||||||
|
|
@ -1064,11 +1067,14 @@ object BuildServerProtocol {
|
||||||
private[server] final class BspCompileState {
|
private[server] final class BspCompileState {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* keeps track of problems in given file so BSP reporter
|
* keeps track of problems in a given file in a map of virtual source file to text documents.
|
||||||
* can omit unnecessary diagnostics updates
|
* In most cases the only text document is the source file. In case of source generation,
|
||||||
|
* e.g. Twirl, the text documents are the input files, e.g. the Twirl files.
|
||||||
|
* We use the sourcePositionMappers to build this map.
|
||||||
*/
|
*/
|
||||||
val hasAnyProblems: java.util.Set[Path] =
|
val problemsBySourceFiles
|
||||||
java.util.concurrent.ConcurrentHashMap.newKeySet[Path]
|
: AtomicReference[Map[VirtualFileRef, Vector[TextDocumentIdentifier]]] =
|
||||||
|
new AtomicReference(Map.empty)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* keeps track of those projects that were compiled at
|
* keeps track of those projects that were compiled at
|
||||||
|
|
@ -1076,6 +1082,6 @@ object BuildServerProtocol {
|
||||||
* are compiled for the first time.
|
* are compiled for the first time.
|
||||||
* see: https://github.com/scalacenter/bloop/issues/726
|
* see: https://github.com/scalacenter/bloop/issues/726
|
||||||
*/
|
*/
|
||||||
val compiledAtLeastOnce: AtomicBoolean = new AtomicBoolean(false)
|
val isFirstReport: AtomicBoolean = new AtomicBoolean(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
package sbt.internal.server
|
package sbt.internal.server
|
||||||
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
import sbt.StandardMain
|
import sbt.StandardMain
|
||||||
import sbt.internal.bsp._
|
import sbt.internal.bsp._
|
||||||
import sbt.internal.util.ManagedLogger
|
import sbt.internal.util.ManagedLogger
|
||||||
|
|
@ -28,6 +26,9 @@ import xsbti.{
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
/**
|
||||||
|
Provides methods for sending success and failure reports and publishing diagnostics.
|
||||||
|
*/
|
||||||
sealed trait BuildServerReporter extends Reporter {
|
sealed trait BuildServerReporter extends Reporter {
|
||||||
private final val sigFilesWritten = "[sig files written]"
|
private final val sigFilesWritten = "[sig files written]"
|
||||||
private final val pureExpression = "a pure expression does nothing in statement position"
|
private final val pureExpression = "a pure expression does nothing in statement position"
|
||||||
|
|
@ -71,10 +72,16 @@ sealed trait BuildServerReporter extends Reporter {
|
||||||
override def comment(pos: XPosition, msg: String): Unit = underlying.comment(pos, msg)
|
override def comment(pos: XPosition, msg: String): Unit = underlying.comment(pos, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bspCompileState what has already been reported in previous compilation.
|
||||||
|
* @param sourcePositionMapper a function that maps an xsbti.Position from the generated source
|
||||||
|
* (the Scala file) to the input file of the generator (e.g. Twirl file)
|
||||||
|
*/
|
||||||
final class BuildServerReporterImpl(
|
final class BuildServerReporterImpl(
|
||||||
buildTarget: BuildTargetIdentifier,
|
buildTarget: BuildTargetIdentifier,
|
||||||
bspCompileState: BspCompileState,
|
bspCompileState: BspCompileState,
|
||||||
converter: FileConverter,
|
converter: FileConverter,
|
||||||
|
sourcePositionMapper: xsbti.Position => xsbti.Position,
|
||||||
protected override val isMetaBuild: Boolean,
|
protected override val isMetaBuild: Boolean,
|
||||||
protected override val logger: ManagedLogger,
|
protected override val logger: ManagedLogger,
|
||||||
protected override val underlying: Reporter
|
protected override val underlying: Reporter
|
||||||
|
|
@ -83,13 +90,13 @@ final class BuildServerReporterImpl(
|
||||||
import sbt.internal.inc.JavaInterfaceUtil._
|
import sbt.internal.inc.JavaInterfaceUtil._
|
||||||
|
|
||||||
private lazy val exchange = StandardMain.exchange
|
private lazy val exchange = StandardMain.exchange
|
||||||
private val problemsByFile = mutable.Map[Path, Vector[Diagnostic]]()
|
private val problemsByFile = mutable.Map[VirtualFileRef, Vector[Problem]]()
|
||||||
|
|
||||||
// sometimes the compiler returns a fake position such as <macro>
|
// sometimes the compiler returns a fake position such as <macro>
|
||||||
// on Windows, this causes InvalidPathException (see #5994 and #6720)
|
// on Windows, this causes InvalidPathException (see #5994 and #6720)
|
||||||
private def toSafePath(ref: VirtualFileRef): Option[Path] =
|
private def toDocument(ref: VirtualFileRef): Option[TextDocumentIdentifier] =
|
||||||
if (ref.id().contains("<")) None
|
if (ref.id().contains("<")) None
|
||||||
else Some(converter.toPath(ref))
|
else Some(TextDocumentIdentifier(converter.toPath(ref).toUri))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send diagnostics from the compilation to the client.
|
* Send diagnostics from the compilation to the client.
|
||||||
|
|
@ -97,65 +104,43 @@ final class BuildServerReporterImpl(
|
||||||
*
|
*
|
||||||
* @param analysis current compile analysis
|
* @param analysis current compile analysis
|
||||||
*/
|
*/
|
||||||
override def sendSuccessReport(
|
override def sendSuccessReport(analysis: CompileAnalysis): Unit = {
|
||||||
analysis: CompileAnalysis,
|
for ((source, infos) <- analysis.readSourceInfos.getAllSourceInfos.asScala) {
|
||||||
): Unit = {
|
val problems = infos.getReportedProblems.toVector
|
||||||
val shouldReportAllProblems = !bspCompileState.compiledAtLeastOnce.getAndSet(true)
|
sendReport(source, problems)
|
||||||
for {
|
}
|
||||||
(source, infos) <- analysis.readSourceInfos.getAllSourceInfos.asScala
|
notifyFirstReport()
|
||||||
filePath <- toSafePath(source)
|
}
|
||||||
} {
|
|
||||||
// clear problems for current file
|
|
||||||
val hadProblems = bspCompileState.hasAnyProblems.remove(filePath)
|
|
||||||
|
|
||||||
val reportedProblems = infos.getReportedProblems.toVector
|
override def sendFailureReport(sources: Array[VirtualFile]): Unit = {
|
||||||
val diagnostics = reportedProblems.map(toDiagnostic)
|
for (source <- sources) {
|
||||||
|
val problems = problemsByFile.getOrElse(source, Vector.empty)
|
||||||
// publish diagnostics if:
|
sendReport(source, problems)
|
||||||
// 1. file had any problems previously - we might want to update them with new ones
|
|
||||||
// 2. file has fresh problems - we might want to update old ones
|
|
||||||
// 3. build project is compiled first time - shouldReportAllProblems is set
|
|
||||||
val shouldPublish = hadProblems || diagnostics.nonEmpty || shouldReportAllProblems
|
|
||||||
|
|
||||||
// file can have some warnings
|
|
||||||
if (diagnostics.nonEmpty) {
|
|
||||||
bspCompileState.hasAnyProblems.add(filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldPublish) {
|
|
||||||
val params = PublishDiagnosticsParams(
|
|
||||||
textDocument = TextDocumentIdentifier(filePath.toUri),
|
|
||||||
buildTarget,
|
|
||||||
originId = None,
|
|
||||||
diagnostics.toVector,
|
|
||||||
reset = true
|
|
||||||
)
|
|
||||||
exchange.notifyEvent("build/publishDiagnostics", params)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override def sendFailureReport(sources: Array[VirtualFile]): Unit = {
|
|
||||||
val shouldReportAllProblems = !bspCompileState.compiledAtLeastOnce.get
|
|
||||||
for {
|
|
||||||
source <- sources
|
|
||||||
filePath <- toSafePath(source)
|
|
||||||
} {
|
|
||||||
val diagnostics = problemsByFile.getOrElse(filePath, Vector.empty)
|
|
||||||
|
|
||||||
val hadProblems = bspCompileState.hasAnyProblems.remove(filePath)
|
private def sendReport(source: VirtualFileRef, problems: Vector[Problem]): Unit = {
|
||||||
val shouldPublish = hadProblems || diagnostics.nonEmpty || shouldReportAllProblems
|
val oldDocuments = getAndClearPreviousDocuments(source)
|
||||||
|
|
||||||
// mark file as file with problems
|
// publish diagnostics if:
|
||||||
if (diagnostics.nonEmpty) {
|
// 1. file had any problems previously: update them with new ones
|
||||||
bspCompileState.hasAnyProblems.add(filePath)
|
// 2. file has fresh problems: report them
|
||||||
}
|
// 3. build project is compiled for the first time: send success report
|
||||||
|
if (oldDocuments.nonEmpty || problems.nonEmpty || isFirstReport) {
|
||||||
|
val diagsByDocuments = problems
|
||||||
|
.flatMap(mapProblemToDiagnostic)
|
||||||
|
.groupBy { case (document, _) => document }
|
||||||
|
.mapValues(_.map { case (_, diag) => diag })
|
||||||
|
updateNewDocuments(source, diagsByDocuments.keys.toVector)
|
||||||
|
|
||||||
if (shouldPublish) {
|
// send a report for the new documents, the old ones and the source file
|
||||||
|
(diagsByDocuments.keySet ++ oldDocuments ++ toDocument(source)).foreach { document =>
|
||||||
|
val diags = diagsByDocuments.getOrElse(document, Vector.empty)
|
||||||
val params = PublishDiagnosticsParams(
|
val params = PublishDiagnosticsParams(
|
||||||
textDocument = TextDocumentIdentifier(filePath.toUri),
|
document,
|
||||||
buildTarget,
|
buildTarget,
|
||||||
originId = None,
|
originId = None,
|
||||||
diagnostics,
|
diags,
|
||||||
reset = true
|
reset = true
|
||||||
)
|
)
|
||||||
exchange.notifyEvent("build/publishDiagnostics", params)
|
exchange.notifyEvent("build/publishDiagnostics", params)
|
||||||
|
|
@ -166,12 +151,13 @@ final class BuildServerReporterImpl(
|
||||||
protected override def publishDiagnostic(problem: Problem): Unit = {
|
protected override def publishDiagnostic(problem: Problem): Unit = {
|
||||||
for {
|
for {
|
||||||
id <- problem.position.sourcePath.toOption
|
id <- problem.position.sourcePath.toOption
|
||||||
filePath <- toSafePath(VirtualFileRef.of(id))
|
(document, diagnostic) <- mapProblemToDiagnostic(problem)
|
||||||
} {
|
} {
|
||||||
val diagnostic = toDiagnostic(problem)
|
val fileRef = VirtualFileRef.of(id)
|
||||||
problemsByFile(filePath) = problemsByFile.getOrElse(filePath, Vector.empty) :+ diagnostic
|
problemsByFile(fileRef) = problemsByFile.getOrElse(fileRef, Vector.empty) :+ problem
|
||||||
|
|
||||||
val params = PublishDiagnosticsParams(
|
val params = PublishDiagnosticsParams(
|
||||||
TextDocumentIdentifier(filePath.toUri),
|
document,
|
||||||
buildTarget,
|
buildTarget,
|
||||||
originId = None,
|
originId = None,
|
||||||
Vector(diagnostic),
|
Vector(diagnostic),
|
||||||
|
|
@ -181,13 +167,51 @@ final class BuildServerReporterImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def toRange(pos: XPosition): Range = {
|
private def getAndClearPreviousDocuments(source: VirtualFileRef): Seq[TextDocumentIdentifier] =
|
||||||
val startLineOpt = pos.startLine.toOption.map(_.toLong - 1)
|
bspCompileState.problemsBySourceFiles.getAndUpdate(_ - source).getOrElse(source, Seq.empty)
|
||||||
val startColumnOpt = pos.startColumn.toOption.map(_.toLong)
|
|
||||||
val endLineOpt = pos.endLine.toOption.map(_.toLong - 1)
|
private def updateNewDocuments(
|
||||||
val endColumnOpt = pos.endColumn.toOption.map(_.toLong)
|
source: VirtualFileRef,
|
||||||
val lineOpt = pos.line.toOption.map(_.toLong - 1)
|
documents: Vector[TextDocumentIdentifier]
|
||||||
val columnOpt = pos.pointer.toOption.map(_.toLong)
|
): Unit = {
|
||||||
|
val _ = bspCompileState.problemsBySourceFiles.updateAndGet(_ + (source -> documents))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def isFirstReport: Boolean = bspCompileState.isFirstReport.get
|
||||||
|
private def notifyFirstReport(): Unit = {
|
||||||
|
val _ = bspCompileState.isFirstReport.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map a given problem, in a Scala source file, to a Diagnostic in an user-facing source file.
|
||||||
|
* E.g. if the source file is generated from Twirl, the diagnostic will be reported to the Twirl file.
|
||||||
|
*/
|
||||||
|
private def mapProblemToDiagnostic(
|
||||||
|
problem: Problem
|
||||||
|
): Option[(TextDocumentIdentifier, Diagnostic)] = {
|
||||||
|
val mappedPosition = sourcePositionMapper(problem.position)
|
||||||
|
for {
|
||||||
|
mappedSource <- mappedPosition.sourcePath.toOption
|
||||||
|
document <- toDocument(VirtualFileRef.of(mappedSource))
|
||||||
|
} yield {
|
||||||
|
val diagnostic = Diagnostic(
|
||||||
|
toRange(mappedPosition),
|
||||||
|
Option(toDiagnosticSeverity(problem.severity)),
|
||||||
|
problem.diagnosticCode().toOption.map(_.code),
|
||||||
|
Option("sbt"),
|
||||||
|
problem.message
|
||||||
|
)
|
||||||
|
(document, diagnostic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def toRange(position: xsbti.Position): Range = {
|
||||||
|
val startLineOpt = position.startLine.toOption.map(_.toLong - 1)
|
||||||
|
val startColumnOpt = position.startColumn.toOption.map(_.toLong)
|
||||||
|
val endLineOpt = position.endLine.toOption.map(_.toLong - 1)
|
||||||
|
val endColumnOpt = position.endColumn.toOption.map(_.toLong)
|
||||||
|
val lineOpt = position.line.toOption.map(_.toLong - 1)
|
||||||
|
val columnOpt = position.pointer.toOption.map(_.toLong)
|
||||||
|
|
||||||
def toPosition(lineOpt: Option[Long], columnOpt: Option[Long]): Option[Position] =
|
def toPosition(lineOpt: Option[Long], columnOpt: Option[Long]): Option[Position] =
|
||||||
lineOpt.map(line => Position(line, columnOpt.getOrElse(0L)))
|
lineOpt.map(line => Position(line, columnOpt.getOrElse(0L)))
|
||||||
|
|
@ -199,45 +223,6 @@ final class BuildServerReporterImpl(
|
||||||
Range(startPos, endPosOpt.getOrElse(startPos))
|
Range(startPos, endPosOpt.getOrElse(startPos))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def toDiagnostic(problem: Problem): Diagnostic = {
|
|
||||||
val actions0 = problem.actions().asScala.toVector
|
|
||||||
val data =
|
|
||||||
if (actions0.isEmpty) None
|
|
||||||
else
|
|
||||||
Some(
|
|
||||||
ScalaDiagnostic(
|
|
||||||
actions = actions0.map { a =>
|
|
||||||
ScalaAction(
|
|
||||||
title = a.title,
|
|
||||||
description = a.description.toOption,
|
|
||||||
edit = Some(
|
|
||||||
ScalaWorkspaceEdit(
|
|
||||||
changes = a.edit.changes().asScala.toVector.map { edit =>
|
|
||||||
ScalaTextEdit(
|
|
||||||
range = toRange(edit.position),
|
|
||||||
newText = edit.newText,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Diagnostic(
|
|
||||||
range = toRange(problem.position),
|
|
||||||
severity = Option(toDiagnosticSeverity(problem.severity)),
|
|
||||||
code = problem.diagnosticCode().toOption.map(_.code),
|
|
||||||
source = Option("sbt"),
|
|
||||||
message = problem.message,
|
|
||||||
relatedInformation = Vector.empty,
|
|
||||||
dataKind = data.map { _ =>
|
|
||||||
"scala"
|
|
||||||
},
|
|
||||||
data = data,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def toDiagnosticSeverity(severity: Severity): Long = severity match {
|
private def toDiagnosticSeverity(severity: Severity): Long = severity match {
|
||||||
case Severity.Info => DiagnosticSeverity.Information
|
case Severity.Info => DiagnosticSeverity.Information
|
||||||
case Severity.Warn => DiagnosticSeverity.Warning
|
case Severity.Warn => DiagnosticSeverity.Warning
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@ lazy val javaProj = project
|
||||||
javacOptions += "-Xlint:all"
|
javacOptions += "-Xlint:all"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
lazy val twirlProj = project
|
||||||
|
.in(file("twirlProj"))
|
||||||
|
.enablePlugins(SbtTwirl)
|
||||||
|
|
||||||
def somethingBad = throw new MessageOnlyException("I am a bad build target")
|
def somethingBad = throw new MessageOnlyException("I am a bad build target")
|
||||||
// other build targets should not be affected by this bad build target
|
// other build targets should not be affected by this bad build target
|
||||||
lazy val badBuildTarget = project.in(file("bad-build-target"))
|
lazy val badBuildTarget = project.in(file("bad-build-target"))
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
addSbtPlugin("com.typesafe.play" % "sbt-twirl" % "1.5.2")
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
@(title: String, paragraphs: Seq[String])
|
||||||
|
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>@title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>@tilte</h1>
|
||||||
|
@for(paragraph <- paragraphs) {
|
||||||
|
<p>@paragraph</p>
|
||||||
|
}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -33,11 +33,11 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|
|
||||||
test("build/initialize") { _ =>
|
test("build/initialize") { _ =>
|
||||||
val id = initializeRequest()
|
val id = initializeRequest()
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
assertMessage(
|
||||||
(s contains s""""id":"${id}"""") &&
|
s""""id":"${id}"""",
|
||||||
(s contains """"resourcesProvider":true""") &&
|
""""resourcesProvider":true""",
|
||||||
(s contains """"outputPathsProvider":true""")
|
""""outputPathsProvider":true"""
|
||||||
})
|
)()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("workspace/buildTargets") { _ =>
|
test("workspace/buildTargets") { _ =>
|
||||||
|
|
@ -102,30 +102,17 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
compile(buildTarget)
|
compile(buildTarget)
|
||||||
|
|
||||||
// This doesn't always come back in 10s on CI.
|
// This doesn't always come back in 10s on CI.
|
||||||
assert(svr.waitForString(60.seconds) { s =>
|
assertMessage("build/taskStart", """"message":"Compiling runAndTest"""")(duration = 60.seconds)
|
||||||
s.contains("build/taskStart") &&
|
assertMessage(
|
||||||
s.contains(""""message":"Compiling runAndTest"""")
|
"build/taskProgress",
|
||||||
})
|
""""message":"Compiling runAndTest (15%)""""
|
||||||
|
)(duration = 60.seconds)
|
||||||
assert(svr.waitForString(60.seconds) { s =>
|
assertMessage(
|
||||||
s.contains("build/taskProgress") &&
|
"build/taskProgress",
|
||||||
s.contains(""""message":"Compiling runAndTest (15%)"""")
|
""""message":"Compiling runAndTest (100%)""""
|
||||||
})
|
)(duration = 60.seconds)
|
||||||
|
assertMessage("build/publishDiagnostics", """"diagnostics":[]""")(duration = 60.seconds)
|
||||||
assert(svr.waitForString(60.seconds) { s =>
|
assertMessage("build/taskFinish", """"message":"Compiled runAndTest"""")(duration = 60.seconds)
|
||||||
s.contains("build/taskProgress") &&
|
|
||||||
s.contains(""""message":"Compiling runAndTest (100%)"""")
|
|
||||||
})
|
|
||||||
|
|
||||||
assert(svr.waitForString(60.seconds) { s =>
|
|
||||||
s.contains("build/publishDiagnostics") &&
|
|
||||||
s.contains(""""diagnostics":[]""")
|
|
||||||
})
|
|
||||||
|
|
||||||
assert(svr.waitForString(60.seconds) { s =>
|
|
||||||
s.contains("build/taskFinish") &&
|
|
||||||
s.contains(""""message":"Compiled runAndTest"""")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test(
|
test(
|
||||||
|
|
@ -136,10 +123,7 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|
|
||||||
compile(buildTarget)
|
compile(buildTarget)
|
||||||
|
|
||||||
assert(svr.waitForString(30.seconds) { s =>
|
assertMessage("build/taskFinish", """"message":"Compiled diagnostics"""")(30.seconds)
|
||||||
s.contains("build/taskFinish") &&
|
|
||||||
s.contains(""""message":"Compiled diagnostics"""")
|
|
||||||
})
|
|
||||||
|
|
||||||
// introduce compile error
|
// introduce compile error
|
||||||
IO.write(
|
IO.write(
|
||||||
|
|
@ -152,13 +136,13 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
reloadWorkspace()
|
reloadWorkspace()
|
||||||
compile(buildTarget)
|
compile(buildTarget)
|
||||||
|
|
||||||
assert(
|
assertMessage(
|
||||||
svr.waitForString(30.seconds) { s =>
|
"build/publishDiagnostics",
|
||||||
s.contains("build/publishDiagnostics") &&
|
"Diagnostics.scala",
|
||||||
s.contains("Diagnostics.scala") &&
|
"\"message\":\"type mismatch"
|
||||||
s.contains("\"message\":\"type mismatch")
|
)(
|
||||||
},
|
duration = 30.seconds,
|
||||||
"should send publishDiagnostics with type error for Main.scala"
|
message = "should send publishDiagnostics with type error for Main.scala"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fix compilation error
|
// fix compilation error
|
||||||
|
|
@ -172,13 +156,13 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
reloadWorkspace()
|
reloadWorkspace()
|
||||||
compile(buildTarget)
|
compile(buildTarget)
|
||||||
|
|
||||||
assert(
|
assertMessage(
|
||||||
svr.waitForString(30.seconds) { s =>
|
"build/publishDiagnostics",
|
||||||
s.contains("build/publishDiagnostics") &&
|
"Diagnostics.scala",
|
||||||
s.contains("Diagnostics.scala") &&
|
"\"diagnostics\":[]"
|
||||||
s.contains("\"diagnostics\":[]")
|
)(
|
||||||
},
|
duration = 30.seconds,
|
||||||
"should send publishDiagnostics with empty diagnostics"
|
message = "should send publishDiagnostics with empty diagnostics"
|
||||||
)
|
)
|
||||||
|
|
||||||
// trigger no-op compilation
|
// trigger no-op compilation
|
||||||
|
|
@ -199,13 +183,13 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|
|
||||||
compile(buildTarget)
|
compile(buildTarget)
|
||||||
|
|
||||||
assert(
|
assertMessage(
|
||||||
svr.waitForString(30.seconds) { s =>
|
"build/publishDiagnostics",
|
||||||
s.contains("build/publishDiagnostics") &&
|
"PatternMatch.scala",
|
||||||
s.contains("PatternMatch.scala") &&
|
""""message":"match may not be exhaustive"""
|
||||||
s.contains(""""message":"match may not be exhaustive""")
|
)(
|
||||||
},
|
duration = 30.seconds,
|
||||||
"should send publishDiagnostics with type error for PatternMatch.scala"
|
message = "should send publishDiagnostics with type error for PatternMatch.scala"
|
||||||
)
|
)
|
||||||
|
|
||||||
IO.write(
|
IO.write(
|
||||||
|
|
@ -223,15 +207,10 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
reloadWorkspace()
|
reloadWorkspace()
|
||||||
compile(buildTarget)
|
compile(buildTarget)
|
||||||
|
|
||||||
assert(
|
assertMessage("build/publishDiagnostics", "PatternMatch.scala", "\"diagnostics\":[]")(
|
||||||
svr.waitForString(30.seconds) { s =>
|
duration = 30.seconds,
|
||||||
s.contains("build/publishDiagnostics") &&
|
message = "should send publishDiagnostics with empty diagnostics"
|
||||||
s.contains("PatternMatch.scala") &&
|
|
||||||
s.contains("\"diagnostics\":[]")
|
|
||||||
},
|
|
||||||
"should send publishDiagnostics with empty diagnostics"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/compile: Java diagnostics") { _ =>
|
test("buildTarget/compile: Java diagnostics") { _ =>
|
||||||
|
|
@ -239,42 +218,32 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|
|
||||||
compile(buildTarget)
|
compile(buildTarget)
|
||||||
|
|
||||||
assert(
|
assertMessage(
|
||||||
svr.waitForString(10.seconds) { s =>
|
"build/publishDiagnostics",
|
||||||
s.contains("build/publishDiagnostics") &&
|
"Hello.java",
|
||||||
s.contains("Hello.java") &&
|
""""severity":2""",
|
||||||
s.contains(""""severity":2""") &&
|
"""missing type arguments for generic class java.util.List"""
|
||||||
s.contains("""missing type arguments for generic class java.util.List""")
|
)(message = "should send publishDiagnostics with severity 2 for Hello.java")
|
||||||
},
|
|
||||||
"should send publishDiagnostics with severity 2 for Hello.java"
|
|
||||||
)
|
|
||||||
|
|
||||||
assert(
|
assertMessage(
|
||||||
svr.waitForString(1.seconds) { s =>
|
"build/publishDiagnostics",
|
||||||
s.contains("build/publishDiagnostics") &&
|
"Hello.java",
|
||||||
s.contains("Hello.java") &&
|
""""severity":1""",
|
||||||
s.contains(""""severity":1""") &&
|
"""incompatible types: int cannot be converted to java.lang.String"""
|
||||||
s.contains("""incompatible types: int cannot be converted to java.lang.String""")
|
)(
|
||||||
},
|
message = "should send publishDiagnostics with severity 1 for Hello.java"
|
||||||
"should send publishDiagnostics with severity 1 for Hello.java"
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/scalacOptions, buildTarget/javacOptions") { _ =>
|
test("buildTarget/scalacOptions, buildTarget/javacOptions") { _ =>
|
||||||
val buildTarget = buildTargetUri("util", "Compile")
|
val buildTarget = buildTargetUri("util", "Compile")
|
||||||
val badBuildTarget = buildTargetUri("badBuildTarget", "Compile")
|
val badBuildTarget = buildTargetUri("badBuildTarget", "Compile")
|
||||||
val id1 = scalacOptions(Seq(buildTarget, badBuildTarget))
|
|
||||||
|
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
val id1 = scalacOptions(Seq(buildTarget, badBuildTarget))
|
||||||
(s contains s""""id":"$id1"""") &&
|
assertMessage(s""""id":"$id1"""", "scala-library-2.13.11.jar")()
|
||||||
(s contains "scala-library-2.13.11.jar")
|
|
||||||
})
|
|
||||||
|
|
||||||
val id2 = javacOptions(Seq(buildTarget, badBuildTarget))
|
val id2 = javacOptions(Seq(buildTarget, badBuildTarget))
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
assertMessage(s""""id":"$id2"""", "scala-library-2.13.11.jar")()
|
||||||
(s contains s""""id":"$id2"""") &&
|
|
||||||
(s contains "scala-library-2.13.11.jar")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/cleanCache") { _ =>
|
test("buildTarget/cleanCache") { _ =>
|
||||||
|
|
@ -328,10 +297,7 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
s"""{ "jsonrpc": "2.0", "id": "$id", "method": "workspace/reload"}"""
|
s"""{ "jsonrpc": "2.0", "id": "$id", "method": "workspace/reload"}"""
|
||||||
)
|
)
|
||||||
assertProcessing("workspace/reload")
|
assertProcessing("workspace/reload")
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
assertMessage(s""""id":"$id"""", """"result":null""")()
|
||||||
(s contains s""""id":"$id"""") &&
|
|
||||||
(s contains """"result":null""")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("workspace/reload: send diagnostic and respond with error") { _ =>
|
test("workspace/reload: send diagnostic and respond with error") { _ =>
|
||||||
|
|
@ -347,22 +313,19 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
)
|
)
|
||||||
val id = reloadWorkspace()
|
val id = reloadWorkspace()
|
||||||
// reload
|
// reload
|
||||||
assert(
|
assertMessage(
|
||||||
svr.waitForString(10.seconds) { s =>
|
s""""buildTarget":{"uri":"$metaBuildTarget"}""",
|
||||||
s.contains(s""""buildTarget":{"uri":"$metaBuildTarget"}""") &&
|
s""""textDocument":{"uri":"${otherBuildFile.toPath.toUri}"}""",
|
||||||
s.contains(s""""textDocument":{"uri":"${otherBuildFile.toPath.toUri}"}""") &&
|
""""severity":1""",
|
||||||
s.contains(""""severity":1""") &&
|
""""reset":true"""
|
||||||
s.contains(""""reset":true""")
|
)()
|
||||||
}
|
|
||||||
)
|
assertMessage(
|
||||||
assert(
|
s""""id":"$id"""",
|
||||||
svr.waitForString(10.seconds) { s =>
|
""""error"""",
|
||||||
s.contains(s""""id":"$id"""") &&
|
s""""code":${ErrorCodes.InternalError}""",
|
||||||
s.contains(""""error"""") &&
|
"Type error in expression"
|
||||||
s.contains(s""""code":${ErrorCodes.InternalError}""") &&
|
)()
|
||||||
s.contains("Type error in expression")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// fix the other-build.sbt file and reload again
|
// fix the other-build.sbt file and reload again
|
||||||
IO.write(
|
IO.write(
|
||||||
otherBuildFile,
|
otherBuildFile,
|
||||||
|
|
@ -374,14 +337,12 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
)
|
)
|
||||||
reloadWorkspace()
|
reloadWorkspace()
|
||||||
// assert received an empty diagnostic
|
// assert received an empty diagnostic
|
||||||
assert(
|
assertMessage(
|
||||||
svr.waitForString(10.seconds) { s =>
|
s""""buildTarget":{"uri":"$metaBuildTarget"}""",
|
||||||
s.contains(s""""buildTarget":{"uri":"$metaBuildTarget"}""") &&
|
s""""textDocument":{"uri":"${otherBuildFile.toPath.toUri}"}""",
|
||||||
s.contains(s""""textDocument":{"uri":"${otherBuildFile.toPath.toUri}"}""") &&
|
""""diagnostics":[]""",
|
||||||
s.contains(""""diagnostics":[]""") &&
|
""""reset":true"""
|
||||||
s.contains(""""reset":true""")
|
)()
|
||||||
}
|
|
||||||
)
|
|
||||||
IO.delete(otherBuildFile)
|
IO.delete(otherBuildFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -395,10 +356,7 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|} }""".stripMargin
|
|} }""".stripMargin
|
||||||
)
|
)
|
||||||
assertProcessing("buildTarget/scalaMainClasses")
|
assertProcessing("buildTarget/scalaMainClasses")
|
||||||
assert(svr.waitForString(30.seconds) { s =>
|
assertMessage(s""""id":"$id"""", """"class":"main.Main"""")(duration = 30.seconds)
|
||||||
(s contains s""""id":"$id"""") &&
|
|
||||||
(s contains """"class":"main.Main"""")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/run") { _ =>
|
test("buildTarget/run") { _ =>
|
||||||
|
|
@ -412,14 +370,8 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|} }""".stripMargin
|
|} }""".stripMargin
|
||||||
)
|
)
|
||||||
assertProcessing("buildTarget/run")
|
assertProcessing("buildTarget/run")
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
assertMessage("build/logMessage", """"message":"Hello World!"""")()
|
||||||
(s contains "build/logMessage") &&
|
assertMessage(s""""id":"$id"""", """"statusCode":1""")()
|
||||||
(s contains """"message":"Hello World!"""")
|
|
||||||
})
|
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
|
||||||
(s contains s""""id":"$id"""") &&
|
|
||||||
(s contains """"statusCode":1""")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/jvmRunEnvironment") { _ =>
|
test("buildTarget/jvmRunEnvironment") { _ =>
|
||||||
|
|
@ -433,15 +385,13 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
)
|
)
|
||||||
assertProcessing("buildTarget/jvmRunEnvironment")
|
assertProcessing("buildTarget/jvmRunEnvironment")
|
||||||
assert {
|
assertMessage(
|
||||||
svr.waitForString(10.seconds) { s =>
|
s""""id":"$id"""",
|
||||||
(s contains s""""id":"$id"""") &&
|
"jsoniter-scala-core_2.13-2.13.11.jar", // compile dependency
|
||||||
(s contains "jsoniter-scala-core_2.13-2.13.11.jar") && // compile dependency
|
"\"jvmOptions\":[\"Xmx256M\"]",
|
||||||
(s contains "\"jvmOptions\":[\"Xmx256M\"]") &&
|
"\"environmentVariables\":{\"KEY\":\"VALUE\"}",
|
||||||
(s contains "\"environmentVariables\":{\"KEY\":\"VALUE\"}") &&
|
"/buildserver/run-and-test/" // working directory
|
||||||
(s contains "/buildserver/run-and-test/") // working directory
|
)()
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/jvmTestEnvironment") { _ =>
|
test("buildTarget/jvmTestEnvironment") { _ =>
|
||||||
|
|
@ -455,16 +405,13 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
)
|
)
|
||||||
assertProcessing("buildTarget/jvmTestEnvironment")
|
assertProcessing("buildTarget/jvmTestEnvironment")
|
||||||
assert {
|
assertMessage(
|
||||||
svr.waitForString(10.seconds) { s =>
|
s""""id":"$id"""",
|
||||||
(s contains s""""id":"$id"""") &&
|
"jsoniter-scala-core_2.13-2.13.11.jar", // compile dependency
|
||||||
// test depends on compile so it has dependencies from both
|
"scalatest_2.13-3.0.8.jar", // test dependency
|
||||||
(s contains "jsoniter-scala-core_2.13-2.13.11.jar") && // compile dependency
|
"\"jvmOptions\":[\"Xmx512M\"]",
|
||||||
(s contains "scalatest_2.13-3.0.8.jar") && // test dependency
|
"\"environmentVariables\":{\"KEY_TEST\":\"VALUE_TEST\"}"
|
||||||
(s contains "\"jvmOptions\":[\"Xmx512M\"]") &&
|
)()
|
||||||
(s contains "\"environmentVariables\":{\"KEY_TEST\":\"VALUE_TEST\"}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/scalaTestClasses") { _ =>
|
test("buildTarget/scalaTestClasses") { _ =>
|
||||||
|
|
@ -477,12 +424,12 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|} }""".stripMargin
|
|} }""".stripMargin
|
||||||
)
|
)
|
||||||
assertProcessing("buildTarget/scalaTestClasses")
|
assertProcessing("buildTarget/scalaTestClasses")
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
assertMessage(
|
||||||
(s contains s""""id":"$id"""") &&
|
s""""id":"$id"""",
|
||||||
(s contains """"tests.FailingTest"""") &&
|
""""tests.FailingTest"""",
|
||||||
(s contains """"tests.PassingTest"""") &&
|
""""tests.PassingTest"""",
|
||||||
(s contains """"framework":"ScalaTest"""")
|
""""framework":"ScalaTest""""
|
||||||
})
|
)()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/test: run all tests") { _ =>
|
test("buildTarget/test: run all tests") { _ =>
|
||||||
|
|
@ -494,10 +441,7 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|} }""".stripMargin
|
|} }""".stripMargin
|
||||||
)
|
)
|
||||||
assertProcessing("buildTarget/test")
|
assertProcessing("buildTarget/test")
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
assertMessage(s""""id":"$id"""", """"statusCode":2""")()
|
||||||
(s contains s""""id":"$id"""") &&
|
|
||||||
(s contains """"statusCode":2""")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/test: run one test class") { _ =>
|
test("buildTarget/test: run one test class") { _ =>
|
||||||
|
|
@ -518,41 +462,38 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|} }""".stripMargin
|
|} }""".stripMargin
|
||||||
)
|
)
|
||||||
assertProcessing("buildTarget/test")
|
assertProcessing("buildTarget/test")
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
assertMessage(s""""id":"$id"""", """"statusCode":1""")()
|
||||||
(s contains s""""id":"$id"""") &&
|
|
||||||
(s contains """"statusCode":1""")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/compile: report error") { _ =>
|
test("buildTarget/compile: report error") { _ =>
|
||||||
val buildTarget = buildTargetUri("reportError", "Compile")
|
val buildTarget = buildTargetUri("reportError", "Compile")
|
||||||
compile(buildTarget)
|
compile(buildTarget)
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
assertMessage(
|
||||||
(s contains s""""buildTarget":{"uri":"$buildTarget"}""") &&
|
s""""buildTarget":{"uri":"$buildTarget"}""",
|
||||||
(s contains """"severity":1""") &&
|
""""severity":1""",
|
||||||
(s contains """"reset":true""")
|
""""reset":true"""
|
||||||
})
|
)()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/compile: report warning") { _ =>
|
test("buildTarget/compile: report warning") { _ =>
|
||||||
val buildTarget = buildTargetUri("reportWarning", "Compile")
|
val buildTarget = buildTargetUri("reportWarning", "Compile")
|
||||||
compile(buildTarget)
|
compile(buildTarget)
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
assertMessage(
|
||||||
(s contains s""""buildTarget":{"uri":"$buildTarget"}""") &&
|
s""""buildTarget":{"uri":"$buildTarget"}""",
|
||||||
(s contains """"severity":2""") &&
|
""""severity":2""",
|
||||||
(s contains """"reset":true""")
|
""""reset":true"""
|
||||||
})
|
)()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/compile: respond error") { _ =>
|
test("buildTarget/compile: respond error") { _ =>
|
||||||
val buildTarget = buildTargetUri("respondError", "Compile")
|
val buildTarget = buildTargetUri("respondError", "Compile")
|
||||||
val id = compile(buildTarget)
|
val id = compile(buildTarget)
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
assertMessage(
|
||||||
s.contains(s""""id":"$id"""") &&
|
s""""id":"$id"""",
|
||||||
s.contains(""""error"""") &&
|
""""error"""",
|
||||||
s.contains(s""""code":${ErrorCodes.InternalError}""") &&
|
s""""code":${ErrorCodes.InternalError}""",
|
||||||
s.contains("custom message")
|
"custom message"
|
||||||
})
|
)()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/resources") { _ =>
|
test("buildTarget/resources") { _ =>
|
||||||
|
|
@ -565,9 +506,7 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|} }""".stripMargin
|
|} }""".stripMargin
|
||||||
)
|
)
|
||||||
assertProcessing("buildTarget/resources")
|
assertProcessing("buildTarget/resources")
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
assertMessage(s""""id":"$id"""", "util/src/main/resources/")()
|
||||||
(s contains s""""id":"$id"""") && (s contains "util/src/main/resources/")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/outputPaths") { _ =>
|
test("buildTarget/outputPaths") { _ =>
|
||||||
|
|
@ -596,6 +535,47 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
assert(actualResult == expectedResult)
|
assert(actualResult == expectedResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("buildTarget/compile: twirl diagnostics (sourcePositionMappers)") { _ =>
|
||||||
|
val buildTarget = buildTargetUri("twirlProj", "Compile")
|
||||||
|
val testFile = new File(svr.baseDirectory, s"twirlProj/src/main/twirl/main.scala.html")
|
||||||
|
|
||||||
|
compile(buildTarget)
|
||||||
|
assertMessage(
|
||||||
|
"build/publishDiagnostics",
|
||||||
|
"main.scala.html",
|
||||||
|
""""severity":1""",
|
||||||
|
"not found: value tilte"
|
||||||
|
)(message = "should report diagnostic in Twirl file")
|
||||||
|
IO.write(
|
||||||
|
testFile,
|
||||||
|
"""|@(title: String, paragraphs: Seq[String])
|
||||||
|
|
|
||||||
|
|<!DOCTYPE HTML>
|
||||||
|
|<html lang="en">
|
||||||
|
| <head>
|
||||||
|
| <title>@title</title>
|
||||||
|
| </head>
|
||||||
|
| <body>
|
||||||
|
| <h1>@title</h1>
|
||||||
|
| @for(paragraph <- paragraphs) {
|
||||||
|
| <p>@paragraph</p>
|
||||||
|
| }
|
||||||
|
| </body>
|
||||||
|
|</html>
|
||||||
|
|""".stripMargin
|
||||||
|
)
|
||||||
|
compile(buildTarget)
|
||||||
|
assertMessage(
|
||||||
|
"build/publishDiagnostics",
|
||||||
|
"main.scala.html",
|
||||||
|
""""diagnostics":[]""",
|
||||||
|
""""reset":true"""
|
||||||
|
)(
|
||||||
|
duration = 30.seconds,
|
||||||
|
message = "should reset diagnostic in Twirl file"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private def initializeRequest(): Int = {
|
private def initializeRequest(): Int = {
|
||||||
val params = InitializeBuildParams(
|
val params = InitializeBuildParams(
|
||||||
"test client",
|
"test client",
|
||||||
|
|
@ -608,11 +588,18 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
sendRequest("build/initialize", params)
|
sendRequest("build/initialize", params)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def assertProcessing(method: String, debug: Boolean = false): Unit = {
|
private def assertProcessing(method: String, debug: Boolean = false): Unit =
|
||||||
assert(svr.waitForString(10.seconds) { msg =>
|
assertMessage("build/logMessage", s""""message":"Processing $method"""")(debug = debug)
|
||||||
if (debug) println(msg)
|
|
||||||
msg.contains("build/logMessage") && msg.contains(s""""message":"Processing $method"""")
|
def assertMessage(
|
||||||
})
|
parts: String*
|
||||||
|
)(duration: FiniteDuration = 10.seconds, debug: Boolean = false, message: String = ""): Unit = {
|
||||||
|
def assertion =
|
||||||
|
svr.waitForString(duration) { msg =>
|
||||||
|
if (debug) println(msg)
|
||||||
|
parts.forall(msg.contains)
|
||||||
|
}
|
||||||
|
if (message.nonEmpty) assert.apply(assertion, message) else assert(assertion)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def reloadWorkspace(): Int =
|
private def reloadWorkspace(): Int =
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue