Skip to content

Commit f159168

Browse files
committed
Compare responses as objects instead of strings
1 parent aaaf448 commit f159168

File tree

9 files changed

+115
-96
lines changed

9 files changed

+115
-96
lines changed

language-server/test/dotty/tools/languageserver/CompletionTest.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ class CompletionTest extends BaseTest {
3232
("y", "Method", "=> Int"),
3333
("notify", "Method", "(): Unit")
3434
)
35-
// FIXME completions is returned in different order in CI
36-
// checkActions(code"class Foo { val xyz: Int = 0; def y: Int = xy${completion(completions)} }")
37-
// checkActions(code"class Foo { val xyz: Int = 0; def y: Int = xy$p1 }".completion(p1, completions))
35+
checkActions(code"class Foo { val xyz: Int = 0; def y: Int = xy${completion(completions)} }")
36+
checkActions(code"class Foo { val xyz: Int = 0; def y: Int = xy$p1 }".completion(p1, completions))
3837
}
3938
}

language-server/test/dotty/tools/languageserver/util/BaseTest.scala

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@ package dotty.tools.languageserver.util
22

33
import java.nio.file.Path
44
import java.nio.file.Paths
5+
import java.util
56

67
import dotty.tools.dotc.util.DiffUtil
78
import dotty.tools.languageserver.util.Code._
89
import dotty.tools.languageserver.util.actions._
9-
import dotty.tools.languageserver.util.embedded.{ActionOnCodePosition, CodePosition}
10+
import dotty.tools.languageserver.util.embedded.CodePosition
1011
import dotty.tools.languageserver.util.server.{TestFile, TestServer}
12+
import org.eclipse.lsp4j._
13+
import org.eclipse.lsp4j.jsonrpc.messages
14+
15+
import scala.collection.JavaConverters._
1116

1217
class BaseTest {
1318

@@ -31,28 +36,36 @@ class BaseTest {
3136

3237
def actionOnPosition(file: TestFile, action: Action, line: Int, char: Int) = {
3338
val response = action match {
34-
case _: CodeHover => testServer.hover(file, line, char).toString
35-
case _: CodeDefinition => testServer.definition(file, line, char).toString
36-
case codeRef: CodeReferences => testServer.references(file, line, char, codeRef.withDecl).toString
39+
case _: CodeHover => testServer.hover(file, line, char)
40+
case _: CodeDefinition => testServer.definition(file, line, char)
41+
case codeRef: CodeReferences => testServer.references(file, line, char, codeRef.withDecl)
3742
case code: CodeCompletion =>
38-
testServer.completion(file, code.position.line, code.position.character).toString
43+
testServer.completion(file, code.position.line, code.position.character)
3944
}
4045

4146
val expected = action.expectedOutput(positions)
4247

43-
if (expected != response) {
44-
45-
val diff = DiffUtil.mkColoredLineDiff(expected.split("\n"), response.split("\n"))
46-
47-
val message = // TODO highlight position in code
48-
s"""When hovering line $line on character $char
49-
|${codeInFiles.map { case (fileName, code) => s"// $fileName\n${code.text}"}.mkString("\n")}
50-
|
51-
|expected output (left) did not match response (right)
52-
|$diff
53-
""".stripMargin
54-
assert(false, message)
48+
def equivalent(e: Any, a: Any): Boolean = (e, a) match {
49+
case _ if e == a => true // Note: equality does not work on messages.Either
50+
case (e: messages.Either[_, _], a: messages.Either[_, _]) =>
51+
equivalent(e.getLeft, a.getLeft) && equivalent(e.getRight, a.getRight)
52+
case (e: Hover, a: Hover) =>
53+
equivalent(e.getContents, a.getContents) && equivalent(e.getRange, a.getRange)
54+
case (e: util.List[_], a: util.List[_]) =>
55+
e.size() == a.size() &&
56+
e.asScala.zip(a.asScala).forall((e2, a2) => equivalent(e2, a2))
57+
case _ =>
58+
println()
59+
println("Equivalence failure")
60+
println(s"Expected (${e.getClass}):")
61+
println(e)
62+
println(s"Actual (${a.getClass}):")
63+
println(a)
64+
false
5565
}
66+
67+
if (!equivalent(expected, response))
68+
fail(codeInFiles, expected, response)
5669
}
5770

5871
val allFilesOpened = codeInFiles.map { case (fileName, code) =>
@@ -67,7 +80,7 @@ class BaseTest {
6780
}
6881
}
6982

70-
def getPositions(codeInFiles: Seq[(String, CodeWithActions)]): PositionContext = {
83+
private def getPositions(codeInFiles: Seq[(String, CodeWithActions)]): PositionContext = {
7184
val posSeq = {
7285
for {
7386
(fileName, code) <- codeInFiles
@@ -79,6 +92,21 @@ class BaseTest {
7992
new PositionContext(posMap)
8093
}
8194

95+
private def fail(codeInFiles: Seq[(String, CodeWithActions)], expected: Object, response: Object) = {
96+
97+
val diff = DiffUtil.mkColoredLineDiff(expected.toString.split("\n"), response.toString.split("\n"))
98+
99+
val message = // TODO add custom message for the current action
100+
s"""When testing actions on:
101+
|
102+
|${codeInFiles.map { case (fileName, code) => s"// $fileName\n${code.text}"}.mkString("\n")}
103+
|
104+
|expected output (left) did not match response (right)
105+
|$diff
106+
|""".stripMargin
107+
assert(false, message)
108+
}
109+
82110
}
83111

84112
object BaseTest {
Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,28 @@
11
package dotty.tools.languageserver.util
22

3+
import java.util
4+
35
import dotty.tools.languageserver.util.actions.CodeReference
6+
import org.eclipse.lsp4j._
47

58
object Responses {
69

7-
def location(refs: List[CodeReference])(implicit ctx: PositionContext): String =
8-
refs.map(ref => Responses.location(ref)).mkString("List(", ", ", ")")
10+
def locations(refs: List[CodeReference])(implicit ctx: PositionContext): util.List[Location] = {
11+
val list = new util.ArrayList[Location]()
12+
for (ref <- refs)
13+
list.add(location(ref))
14+
list
15+
}
916

10-
def location(ref: CodeReference)(implicit ctx: PositionContext): String = {
17+
def location(ref: CodeReference)(implicit ctx: PositionContext): Location = {
1118
ref.check()
12-
val start = ref.range.start
13-
val end = ref.range.end
14-
s"""Location [
15-
| uri = "file://${BaseTest.sourceDir}/${ref.fileName}"
16-
| range = Range [
17-
| start = Position [
18-
| line = ${start.line}
19-
| character = ${start.character}
20-
| ]
21-
| end = Position [
22-
| line = ${end.line}
23-
| character = ${end.character}
24-
| ]
25-
| ]
26-
|]""".stripMargin
19+
new Location(
20+
s"file://${BaseTest.sourceDir}/${ref.fileName}",
21+
new Range(
22+
new Position(ref.range.start.line, ref.range.start.character),
23+
new Position(ref.range.end.line, ref.range.end.character)
24+
)
25+
)
2726
}
2827

29-
def emptyHover: String =
30-
s"""Hover [
31-
| contents = null
32-
| range = null
33-
|]""".stripMargin
34-
35-
def hover(expected: String): String =
36-
s"""Hover [
37-
| contents = SeqWrapper (
38-
| Either [
39-
| left = $expected
40-
| right = null
41-
| ]
42-
| )
43-
| range = null
44-
|]""".stripMargin
45-
4628
}

language-server/test/dotty/tools/languageserver/util/actions/Action.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ package dotty.tools.languageserver.util.actions
33
import dotty.tools.languageserver.util.PositionContext
44

55
trait Action {
6-
def expectedOutput(implicit posCtx: PositionContext): String
6+
def expectedOutput(implicit posCtx: PositionContext): Object
77
def onEachPosition(f: (Int, Int) => Unit)(implicit posCtx: PositionContext): Unit
88
}
Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,28 @@
11
package dotty.tools.languageserver.util.actions
22

33
import dotty.tools.languageserver.util.PositionContext
4-
import dotty.tools.languageserver.util.Responses._
54
import dotty.tools.languageserver.util.embedded.CodePosition
65

6+
import scala.collection.JavaConverters._
7+
8+
import org.eclipse.lsp4j._
9+
import org.eclipse.lsp4j.jsonrpc.messages._
10+
711
case class CodeCompletion(position: CodePosition, completions: List[(String, String, String)]) extends ActionOnPosition {
812

9-
def expectedOutput(implicit posCtx: PositionContext): String =
10-
s"""Either [
11-
| left = null
12-
| right = CompletionList [
13-
| isIncomplete = false
14-
| items = SeqWrapper (
15-
|${completions.map(comp => completionItem(comp._1, comp._2, comp._3)).mkString(",\n")}
16-
| )
17-
|]
18-
|]""".stripMargin
13+
def expectedOutput(implicit posCtx: PositionContext): Either[_, _] = {
14+
Either.forRight(
15+
new CompletionList(
16+
false,
17+
completions.map(comp => completionItem(comp._1, comp._2, comp._3)).asJava)
18+
)
19+
}
1920

20-
def completionItem(label: String, kind: String, detail: String): String =
21-
s""" CompletionItem [
22-
| label = "$label"
23-
| kind = $kind
24-
| detail = "$detail"
25-
| documentation = null
26-
| sortText = null
27-
| filterText = null
28-
| insertText = null
29-
| insertTextFormat = null
30-
| textEdit = null
31-
| additionalTextEdits = null
32-
| command = null
33-
| data = null
34-
| ]""".stripMargin
21+
private def completionItem(label: String, kind: String, detail: String): CompletionItem = {
22+
val ci = new CompletionItem()
23+
ci.setLabel(label)
24+
ci.setKind(CompletionItemKind.valueOf(kind))
25+
ci.setDetail(detail)
26+
ci
27+
}
3528
}
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package dotty.tools.languageserver.util.actions
22

3-
import dotty.tools.languageserver.util.{PositionContext, CodeRange, Responses}
3+
import java.util
4+
5+
import dotty.tools.languageserver.util._
6+
import org.eclipse.lsp4j._
7+
48

59
case class CodeDefinition(text: String, refOpt: Option[CodeReference], range: CodeRange) extends ActionOnRange {
6-
override def expectedOutput(implicit posCtx: PositionContext): String =
7-
Responses.location(refOpt.toList)
10+
override def expectedOutput(implicit posCtx: PositionContext): util.List[Location] =
11+
Responses.locations(refOpt.toList)
812
}
Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
package dotty.tools.languageserver.util.actions
22

3-
import dotty.tools.languageserver.util.Responses._
4-
import dotty.tools.languageserver.util.{PositionContext, CodeRange}
3+
4+
import dotty.tools.languageserver.util.{CodeRange, PositionContext}
5+
import org.eclipse.lsp4j.{Hover, MarkedString}
6+
import org.eclipse.lsp4j.jsonrpc.messages._
7+
8+
import scala.collection.JavaConverters._
59

610
case class CodeHover(expected: String, range: CodeRange) extends ActionOnRange {
7-
override def expectedOutput(implicit posCtx: PositionContext): String =
8-
if (expected.isEmpty) emptyHover else hover(expected)
11+
override def expectedOutput(implicit posCtx: PositionContext): Hover = {
12+
val hover = new Hover
13+
if (expected != "") {
14+
hover.setContents(List(Either.forLeft[String, MarkedString](expected)).asJava)
15+
}
16+
hover
17+
}
918
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package dotty.tools.languageserver.util.actions
22

3-
import dotty.tools.languageserver.util.{PositionContext, CodeRange, Responses}
3+
import java.util
4+
5+
import dotty.tools.languageserver.util.{CodeRange, PositionContext, Responses}
6+
import org.eclipse.lsp4j._
47

58
case class CodeReferences(text: String, refs: List[CodeReference], withDecl: Boolean, range: CodeRange) extends ActionOnRange {
6-
def expectedOutput(implicit posCtx: PositionContext): String = Responses.location(refs)
9+
def expectedOutput(implicit posCtx: PositionContext): util.List[Location] = Responses.locations(refs)
710
}

language-server/test/dotty/tools/languageserver/util/server/TestServer.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dotty.tools.languageserver.util.server
33
import java.io.PrintWriter
44
import java.net.URI
55
import java.nio.file.Path
6+
import java.util
67

78
import dotty.tools.languageserver.DottyLanguageServer
89
import org.eclipse.lsp4j._
@@ -56,18 +57,18 @@ class TestServer(testFolder: Path) {
5657
testFile
5758
}
5859

59-
def definition(file: TestFile, line: Int, character: Int): List[Location] =
60-
server.definition(pos(file, line, character)).get().asScala.toList
60+
def definition(file: TestFile, line: Int, character: Int): util.List[Location] =
61+
server.definition(pos(file, line, character)).get().asInstanceOf[util.List[Location]]
6162

6263
def hover(file: TestFile, line: Int, character: Int): Hover =
6364
server.hover(pos(file, line, character)).get()
6465

65-
def references(file: TestFile, line: Int, character: Int, withDecl: Boolean): List[Location] = {
66+
def references(file: TestFile, line: Int, character: Int, withDecl: Boolean): util.List[Location] = {
6667
val rp = new ReferenceParams()
6768
rp.setContext(new ReferenceContext(withDecl))
6869
rp.setTextDocument(file.textDocumentIdentifier)
6970
rp.setPosition(new Position(line, character))
70-
server.references(rp).get().asScala.toList
71+
server.references(rp).get().asInstanceOf[util.List[Location]]
7172
}
7273

7374
def completion(file: TestFile, line: Int, character: Int) =

0 commit comments

Comments
 (0)