Skip to content

Commit 82003e6

Browse files
Merge pull request #161 from alexarchambault/bump-jsonrpc4s
Update jsonrpc4s (via a fork)
2 parents 1b24bf2 + 3b113c5 commit 82003e6

File tree

6 files changed

+176
-13
lines changed

6 files changed

+176
-13
lines changed

.github/workflows/ci.yml

-4
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,9 @@ jobs:
8484
with:
8585
jvm: "zulu:17"
8686
- run: |
87-
./mill -i __.publishLocal &&\
8887
./mill -i integration.test.jvm
8988
if: runner.os != 'Windows'
9089
- run: |
91-
@call ./mill.bat -i __.publishLocal
9290
@call ./mill.bat -i integration.test.jvm
9391
shell: cmd
9492
if: runner.os == 'Windows'
@@ -127,11 +125,9 @@ jobs:
127125
if-no-files-found: error
128126
retention-days: 1
129127
- run: |
130-
./mill -i __.publishLocal &&\
131128
./mill -i "integration.test.native"
132129
if: github.event_name == 'push' && runner.os != 'Windows'
133130
- run: |
134-
./mill.bat -i __.publishLocal
135131
./mill.bat -i integration.test.native
136132
if: github.event_name == 'push' && runner.os == 'Windows'
137133
shell: bash

build.sc

+26-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ object Dependencies {
1919

2020
def scalaVersions = Seq(scala212, scala213)
2121

22+
def serverScalaVersion = scala212
23+
2224
def asmVersion = "9.6"
2325
def coursierVersion = "2.1.0-M6-53-gb4f448130"
2426
def graalvmVersion = "22.2.0"
@@ -46,6 +48,7 @@ object Dependencies {
4648
ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core:$jsoniterVersion"
4749
def jsoniterMacros =
4850
ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:$jsoniterVersion"
51+
def jsonrpc4s = ivy"io.github.alexarchambault.bleep::jsonrpc4s:0.1.1"
4952
def junit = ivy"com.github.sbt:junit-interface:0.13.3"
5053
def libdaemonjvm = ivy"io.github.alexarchambault.libdaemon::libdaemon:0.0.11"
5154
def libraryManagement = ivy"org.scala-sbt::librarymanagement-ivy:1.9.3"
@@ -156,7 +159,11 @@ class Shared(val crossScalaVersion: String) extends BloopCrossSbtModule with Pub
156159
emptyZip()
157160
}
158161
def ivyDeps = super.ivyDeps() ++ Agg(
159-
Dependencies.bsp4s.exclude(("com.github.plokhotnyuk.jsoniter-scala", "*")),
162+
Dependencies.bsp4s
163+
.exclude(("com.github.plokhotnyuk.jsoniter-scala", "*"))
164+
.exclude(("me.vican.jorge", "jsonrpc4s_2.12"))
165+
.exclude(("me.vican.jorge", "jsonrpc4s_2.13")),
166+
Dependencies.jsonrpc4s,
160167
Dependencies.coursierInterface,
161168
Dependencies.jsoniterCore,
162169
Dependencies.log4j,
@@ -532,8 +539,8 @@ object `bloop-rifle` extends BloopCliModule {
532539
|
533540
|/** Build-time constants. Generated by mill. */
534541
|object Constants {
535-
| def bloopVersion = "${frontend(Dependencies.scala212).publishVersion()}"
536-
| def bloopScalaVersion = "${Dependencies.scala212}"
542+
| def bloopVersion = "${frontend(Dependencies.serverScalaVersion).publishVersion()}"
543+
| def bloopScalaVersion = "${Dependencies.serverScalaVersion}"
537544
| def bspVersion = "${Dependencies.bsp4j.dep.version}"
538545
|}
539546
|""".stripMargin
@@ -607,6 +614,19 @@ object integration extends BloopCliModule {
607614
Dependencies.pprint
608615
)
609616

617+
private def repoRoot = os.pwd / "out" / "repo"
618+
def localRepo = T {
619+
val modules = Seq(
620+
frontend(Dependencies.serverScalaVersion),
621+
backend(Dependencies.serverScalaVersion),
622+
shared(Dependencies.serverScalaVersion)
623+
)
624+
os.remove.all(repoRoot)
625+
os.makeDir.all(repoRoot)
626+
val tasks = modules.map(_.publishLocalNoFluff(repoRoot.toString))
627+
define.Target.sequence(tasks)
628+
}
629+
610630
def forkEnv = super.forkEnv() ++ Seq(
611631
"BLOOP_CLI_TESTS_TMP_DIR" -> tmpDir()
612632
)
@@ -615,8 +635,10 @@ object integration extends BloopCliModule {
615635
def test(args: String*) = {
616636
val argsTask = T.task {
617637
val launcher = launcherTask().path
638+
localRepo()
618639
val extraArgs = Seq(
619-
s"-Dtest.bloop-cli.path=$launcher"
640+
s"-Dtest.bloop-cli.path=$launcher",
641+
s"-Dtest.bloop-cli.repo=$repoRoot"
620642
)
621643
args ++ extraArgs
622644
}

frontend/src/main/scala/bloop/bsp/BspServer.scala

+2
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ object BspServer {
163163
case scala.util.Success(socket: ServerSocket) =>
164164
listenToConnection(handle, socket).onErrorRecover {
165165
case t =>
166+
System.err.println("Exiting BSP server with:")
167+
t.printStackTrace(System.err)
166168
state.withError(s"Exiting BSP server with ${t.getMessage}", t)
167169
}
168170
case scala.util.Failure(t: Throwable) =>

integration/test/src/bloop/cli/integration/BloopCliTests.scala

+4-5
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,21 @@ class BloopCliTests extends munit.FunSuite {
99
val dirArgs = Seq[os.Shellable]("--daemon-dir", root / "daemon")
1010

1111
os.proc(Launcher.launcher, "exit", dirArgs)
12-
.call(cwd = root)
12+
.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)
1313
val statusCheck0 = os.proc(Launcher.launcher, "status", dirArgs)
1414
.call(cwd = root)
1515
.out.trim()
1616
expect(statusCheck0 == "stopped")
1717

18-
val res = os.proc(Launcher.launcher, dirArgs, "about")
19-
.call(cwd = root)
20-
expect(res.out.text().startsWith("bloop v"))
18+
os.proc(Launcher.launcher, dirArgs, "about")
19+
.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit, env = Launcher.extraEnv)
2120
val statusCheck1 = os.proc(Launcher.launcher, "status", dirArgs)
2221
.call(cwd = root)
2322
.out.trim()
2423
expect(statusCheck1 == "running")
2524

2625
os.proc(Launcher.launcher, "exit", dirArgs)
27-
.call(cwd = root)
26+
.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)
2827
val statusCheck2 = os.proc(Launcher.launcher, "status", dirArgs)
2928
.call(cwd = root)
3029
.out.trim()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package bloop.cli.integration
2+
3+
import java.net.{StandardProtocolFamily, UnixDomainSocketAddress}
4+
import java.nio.channels.SocketChannel
5+
import java.nio.charset.StandardCharsets
6+
import java.nio.ByteBuffer
7+
8+
class BspTests extends munit.FunSuite {
9+
10+
test("no JSON junk in errors") {
11+
TmpDir.fromTmpDir { root =>
12+
val dirArgs = Seq[os.Shellable]("--daemon-dir", root / "daemon")
13+
val bspFile = root / "bsp-socket"
14+
15+
val dummyMsg =
16+
"""{
17+
| "jsonrpc": "2.0",
18+
| "method": "workspace/buildTargetz",
19+
| "params": null,
20+
| "id": 2
21+
|}""".stripMargin
22+
23+
var bspProc: os.SubProcess = null
24+
var socket: SocketChannel = null
25+
26+
try {
27+
os.proc(Launcher.launcher, dirArgs, "about")
28+
.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit, env = Launcher.extraEnv)
29+
30+
bspProc =
31+
os.proc(Launcher.launcher, dirArgs, "bsp", "--protocol", "local", "--socket", bspFile)
32+
.spawn(cwd = root, stdin = os.Inherit, stdout = os.Inherit)
33+
34+
val addr = UnixDomainSocketAddress.of(bspFile.toNIO)
35+
var connected = false
36+
var attemptCount = 0
37+
38+
while (!connected && bspProc.isAlive() && attemptCount < 10) {
39+
if (attemptCount > 0)
40+
Thread.sleep(1000L)
41+
attemptCount += 1
42+
43+
if (os.exists(bspFile)) {
44+
socket = SocketChannel.open(StandardProtocolFamily.UNIX)
45+
socket.connect(addr)
46+
socket.finishConnect()
47+
connected = true
48+
}
49+
}
50+
51+
if (!connected)
52+
sys.error("Not connected to Bloop server via BSP :|")
53+
54+
def sendMsg(msg: String): Unit = {
55+
val bytes = msg.getBytes(StandardCharsets.UTF_8)
56+
57+
def doWrite(buf: ByteBuffer): Unit =
58+
if (buf.position() < buf.limit()) {
59+
val written = socket.write(buf)
60+
if (written == 0)
61+
Thread.sleep(100L)
62+
doWrite(buf)
63+
}
64+
65+
doWrite(ByteBuffer.wrap(
66+
(s"Content-Length: ${bytes.length}" + "\r\n\r\n").getBytes(StandardCharsets.UTF_8)
67+
))
68+
doWrite(ByteBuffer.wrap(bytes))
69+
}
70+
71+
sendMsg(dummyMsg)
72+
73+
val arr = Array.ofDim[Byte](10 * 1024)
74+
val buf = ByteBuffer.wrap(arr)
75+
76+
// seems to do the job…
77+
def doRead(buf: ByteBuffer, count: Int): Int = {
78+
val read = socket.read(buf)
79+
if (read <= 0 && count < 100) {
80+
Thread.sleep(100L)
81+
doRead(buf, count + 1)
82+
}
83+
else
84+
read
85+
}
86+
87+
val read = doRead(buf, 0)
88+
assert(read > 0)
89+
90+
val resp = new String(arr, 0, read, StandardCharsets.UTF_8)
91+
92+
def validateJson(content: String): Boolean = {
93+
assert(content.startsWith("{"))
94+
assert(content.endsWith("}"))
95+
val chars = content.toCharArray
96+
val objCount = Array.ofDim[Int](chars.length)
97+
for (i <- 0 until chars.length) {
98+
val previous = if (i == 0) 0 else objCount(i - 1)
99+
val count = previous + (if (chars(i) == '{') 1 else if (chars(i) == '}') -1 else 0)
100+
objCount(i) = count
101+
}
102+
objCount.dropRight(1).forall(_ > 0)
103+
}
104+
105+
resp.linesWithSeparators.toVector match {
106+
case Seq(cl, empty, other @ _*)
107+
if cl.startsWith("Content-Length:") && empty.trim.isEmpty =>
108+
val json = other.mkString
109+
val validated = validateJson(json)
110+
if (!validated)
111+
pprint.err.log(json)
112+
assert(validated, "Unexpected JSON response shape")
113+
case _ =>
114+
pprint.err.log(resp)
115+
sys.error("Unexpected response shape")
116+
}
117+
}
118+
finally {
119+
if (socket != null)
120+
socket.close()
121+
122+
if (bspProc != null) {
123+
bspProc.waitFor(1000L)
124+
if (bspProc.isAlive())
125+
bspProc.close()
126+
127+
os.proc(Launcher.launcher, dirArgs, "exit")
128+
.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit, check = false)
129+
}
130+
}
131+
}
132+
}
133+
134+
}

integration/test/src/bloop/cli/integration/Launcher.scala

+10
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,14 @@ object Launcher {
77
sys.error("test.bloop-cli.path not set")
88
)
99

10+
lazy val repoRoot = os.Path(sys.props.getOrElse(
11+
"test.bloop-cli.repo",
12+
sys.error("test.bloop-cli.repo not set")
13+
))
14+
15+
lazy val extraEnv =
16+
Map(
17+
"COURSIER_REPOSITORIES" -> s"ivy:${repoRoot.toNIO.toUri.toASCIIString}/[defaultPattern]|ivy2Local|central"
18+
)
19+
1020
}

0 commit comments

Comments
 (0)