From a1d1a644980158011e6ae680cdb918d95a9808f9 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 24 Dec 2018 17:58:55 +0100 Subject: [PATCH 1/3] Implement Tokio-based test LSP client --- Cargo.lock | 359 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 5 + tests/client.rs | 111 +++++++++++++ tests/support/client.rs | 124 ++++++++++++++ tests/support/mod.rs | 1 + 5 files changed, 600 insertions(+) create mode 100644 tests/client.rs create mode 100644 tests/support/client.rs diff --git a/Cargo.lock b/Cargo.lock index 9d57626e7bf..d2f0898e3bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,11 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "arc-swap" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "arrayvec" version = "0.4.10" @@ -84,6 +89,15 @@ name = "byteorder" version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bytes" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bytesize" version = "1.0.0" @@ -287,6 +301,15 @@ dependencies = [ "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-deque" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-epoch" version = "0.3.1" @@ -301,6 +324,19 @@ dependencies = [ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-epoch" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-utils" version = "0.2.2" @@ -652,6 +688,15 @@ dependencies = [ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "iovec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itertools" version = "0.7.11" @@ -803,6 +848,16 @@ dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lsp-codec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "matches" version = "0.1.8" @@ -851,6 +906,56 @@ dependencies = [ "miniz_oxide 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mio" +version = "0.6.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-named-pipes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-uds" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "miow" version = "0.3.3" @@ -860,6 +965,16 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nodrop" version = "0.1.13" @@ -1223,12 +1338,14 @@ dependencies = [ "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "home 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 9.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "languageserver-types 0.53.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lsp-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "ordslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "racer 2.1.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1249,6 +1366,9 @@ dependencies = [ "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-process 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1599,6 +1719,20 @@ name = "shell-escape" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "signal-hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arc-swap 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "slab" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "smallvec" version = "0.6.7" @@ -1715,6 +1849,194 @@ dependencies = [ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tokio" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-codec" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-current-thread" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-fs" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-process" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-signal 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-signal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "signal-hook 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-timer" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-udp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-uds" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "toml" version = "0.4.10" @@ -1869,10 +2191,20 @@ dependencies = [ "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum arc-swap 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1025aeae2b664ca0ea726a89d574fe8f4e77dd712d443236ad1de00379450cf6" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" @@ -1882,6 +2214,7 @@ dependencies = [ "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum bytecount 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b92204551573580e078dc80017f36a213eb77a0450e4ddd8cfa0f3f2d1f0178f" "checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" +"checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" "checksum bytesize 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "716960a18f978640f25101b5cbf1c6f6b0d3192fab36a2d98ca96f0ecbe41010" "checksum cargo 0.34.0 (git+https://github.com/rust-lang/cargo?rev=2a9f16da7ffc4877aacf7547bac705f0d82de2d6)" = "" "checksum cargo_metadata 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1b4d380e1bab994591a24c2bdd1b054f64b60bef483a8c598c7c345bc3bbe" @@ -1899,7 +2232,9 @@ dependencies = [ "checksum crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e91d5240c6975ef33aeb5f148f35275c25eda8e8a5f95abe421978b05b8bf192" "checksum crossbeam-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "137bc235f622ffaa0428e3854e24acb53291fc0b3ff6fb2cb75a8be6fb02f06b" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" +"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" +"checksum crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f10a4f8f409aaac4b16a5474fb233624238fcdeefb9ba50d5ea059aab63ba31c" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a" "checksum crypto-hash 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "20ff87d28defc77c9980a5b81cae1a33c791dd0ead8af0cee0833eb98c8305b9" @@ -1939,6 +2274,7 @@ dependencies = [ "checksum if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4bac95d9aa0624e7b78187d6fb8ab012b41d9f6f54b1bcb61e61c4845f8357ec" "checksum ignore 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ad03ca67dc12474ecd91fdb94d758cbd20cb4e7a78ebe831df26a9b7511e1162" "checksum im-rc 12.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4591152fd573cf453a890b5f9fdc5c328a751a0785539316739d5f85e5c468c" +"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" @@ -1956,13 +2292,19 @@ dependencies = [ "checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum lsp-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b29e3d1632fef13c1286b0b2f8545a7d894ae565a7fac013b90a17ee5bfbc91" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum miniz-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0300eafb20369952951699b68243ab4334f4b10a88f411c221d444b36c40e649" "checksum miniz_oxide 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ad30a47319c16cde58d0314f5d98202a80c9083b5f61178457403dfb14e509c" "checksum miniz_oxide_c_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "28edaef377517fd9fe3e085c37d892ce7acd1fbeab9239c5a36eec352d8a8b7e" +"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" +"checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" +"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum num-derive 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8af1847c907c2f04d7bfd572fb25bbb4385c637fe5be163cf2f8c5d778fe1e7d" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" @@ -2040,6 +2382,8 @@ dependencies = [ "checksum serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "190e9765dcedb56be63b6e0993a006c7e3b071a016a304736e4a315dc01fb142" "checksum serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)" = "bdf540260cfee6da923831f4776ddc495ada940c30117977c70f1313a6130545" "checksum shell-escape 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "170a13e64f2a51b77a45702ba77287f5c6829375b04a69cf2222acd17d0cfab9" +"checksum signal-hook 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1f272d1b7586bec132ed427f532dd418d8beca1ca7f2caf7df35569b1415a4b4" +"checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" "checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db" "checksum socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" @@ -2053,6 +2397,20 @@ dependencies = [ "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum tokio 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4790d0be6f4ba6ae4f48190efa2ed7780c9e3567796abdb285003cf39840d9c5" +"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" +"checksum tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "331c8acc267855ec06eb0c94618dcbbfea45bed2d20b77252940095273fb58f6" +"checksum tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30c6dbf2d1ad1de300b393910e8a3aa272b724a400b6531da03eed99e329fbf0" +"checksum tokio-fs 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0e9cbbc8a3698b7ab652340f46633364f9eaa928ddaaee79d8b8f356dd79a09d" +"checksum tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b53aeb9d3f5ccf2ebb29e19788f96987fa1355f8fe45ea193928eaaaf3ae820f" +"checksum tokio-process 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88e1281e412013f1ff5787def044a9577a0bed059f451e835f1643201f8b777d" +"checksum tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "afbcdb0f0d2a1e4c440af82d7bbf0bf91a8a8c0575bcd20c05d15be7e9d3a02f" +"checksum tokio-signal 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "dd6dc5276ea05ce379a16de90083ec80836440d5ef8a6a39545a3207373b8296" +"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" +"checksum tokio-threadpool 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "17465013014410310f9f61fa10bf4724803c149ea1d51efece131c38efca93aa" +"checksum tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4f37f0111d76cc5da132fe9bc0590b9b9cfd079bc7e75ac3846278430a299ff8" +"checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" +"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" @@ -2077,3 +2435,4 @@ dependencies = [ "checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/Cargo.toml b/Cargo.toml index 930b130ae16..63e00f457eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,11 @@ rustc-workspace-hack = "1.0.0" [dev-dependencies] difference = "2" tempfile = "3" +lsp-codec = "0.1" +tokio = "0.1" +futures = "0.1" +tokio-process = "0.2" +tokio-timer = "0.2" [build-dependencies] rustc_tools_util = "0.1.1" diff --git a/tests/client.rs b/tests/client.rs new file mode 100644 index 00000000000..3fe00ecded3 --- /dev/null +++ b/tests/client.rs @@ -0,0 +1,111 @@ +use serde_json::{self, json}; + +use self::support::project_builder::project; + +#[allow(dead_code)] +mod support; + +#[test] +fn client_test_simple_workspace() { + let p = project("simple_workspace") + .file( + "Cargo.toml", + r#" + [workspace] + members = [ + "member_lib", + "member_bin", + ] + "#, + ) + .file( + "Cargo.lock", + r#" + [root] + name = "member_lib" + version = "0.1.0" + + [[package]] + name = "member_bin" + version = "0.1.0" + dependencies = [ + "member_lib 0.1.0", + ] + "#, + ) + .file( + "member_bin/Cargo.toml", + r#" + [package] + name = "member_bin" + version = "0.1.0" + authors = ["Igor Matuszewski "] + + [dependencies] + member_lib = { path = "../member_lib" } + "#, + ) + .file( + "member_bin/src/main.rs", + r#" + extern crate member_lib; + + fn main() { + let a = member_lib::MemberLibStruct; + } + "#, + ) + .file( + "member_lib/Cargo.toml", + r#" + [package] + name = "member_lib" + version = "0.1.0" + authors = ["Igor Matuszewski "] + + [dependencies] + "#, + ) + .file( + "member_lib/src/lib.rs", + r#" + pub struct MemberLibStruct; + + struct Unused; + + #[cfg(test)] + mod tests { + #[test] + fn it_works() { + } + } + "#, + ) + .build(); + + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.send(json!({ + "jsonrpc": "2.0", + "id": 0, + "method": "initialize", + "params": { + "rootPath": root_path, + "capabilities": {} + } + })); + + rls.wait_for_indexing(); + + // Check if we built member_lib and member_bin + their cfg(test) variants + let count = rls.messages() + .iter() + .filter(|msg| msg["method"] == "window/progress") + .filter(|msg| msg["params"]["title"] == "Building") + .filter(|msg| msg["params"]["message"].as_str().map(|x| x.starts_with("member_")).unwrap_or(false)) + .count(); + assert_eq!(count, 4); + + rls.shutdown(); +} diff --git a/tests/support/client.rs b/tests/support/client.rs new file mode 100644 index 00000000000..3fd963c9933 --- /dev/null +++ b/tests/support/client.rs @@ -0,0 +1,124 @@ +//! Tokio-based LSP client. The tokio `current_thread::Runtime` allows for a +//! cheap, single-threaded blocking until a certain message is received from the +//! server. It also allows enforcing timeouts, which are necessary for testing. + +use std::process::{Command, Stdio}; +use std::time::Duration; + +use futures::sink::Sink; +use futures::stream::Stream; +use lsp_codec::{LspDecoder, LspEncoder}; +use serde_json::{json, Value}; +use tokio::codec::{FramedRead, FramedWrite}; +use tokio::runtime::current_thread::Runtime; +use tokio_process::{Child, ChildStdin, ChildStdout, CommandExt}; +use tokio_timer::Timeout; + +use super::project_builder::Project; +use super::rls_exe; + +impl Project { + pub fn spawn_rls_async(&self) -> RlsHandle { + let mut cmd = Command::new(rls_exe()); + cmd.current_dir(self.root()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()); + + let mut child = cmd.spawn_async().expect("Couldn't spawn RLS"); + let stdin = child.stdin().take().unwrap(); + let stdout = child.stdout().take().unwrap(); + + let reader = FramedRead::new(std::io::BufReader::new(stdout), LspDecoder::default()); + let writer = Some(FramedWrite::new(stdin, LspEncoder)); + + RlsHandle { + reader, + writer, + child, + runtime: Runtime::new().unwrap(), + messages: Vec::new(), + } + } +} + +/// Holds the handle to a spawned RLS child process and allows to send and +/// receive messages to and from the process. +pub struct RlsHandle { + /// Asynchronous LSP reader for the spawned process + reader: FramedRead, LspDecoder>, + /// ASynchronous LSP writer for the spawned process + writer: Option>, + /// Handle to the spawned child + child: Child, + /// Tokio single-thread runtime required for interaction with async-based + /// `reader` and `writer` + runtime: Runtime, + /// LSP Messages received from the stream and processed + messages: Vec, +} + +impl RlsHandle { + /// Returns messages received until the moment of the call. + pub fn messages(&self) -> &[Value] { + &self.messages + } + + // TODO: Notify on every message received? + fn receive_messages(&mut self, msgs: Vec) { + for msg in &msgs { + eprintln!("Received: {:?}", msg); + } + + self.messages.extend(msgs); + } + + /// Synchronously sends a message to the RLS. + pub fn send(&mut self, msg: Value) { + let writer = self.writer.take().unwrap(); + + let fut = writer.send(msg); + + self.writer = Some(self.runtime.block_on(fut).unwrap()); + } + + /// Consumes messages in blocking manner until `f` predicate returns true + /// for a received message from the stream. + pub fn take_messages_until(&mut self, f: impl Fn(&Value) -> bool) -> &[Value] { + let stream = self.reader.by_ref(); + let old_msg_len = self.messages.len(); + + let msgs = stream.take_while(|msg| Ok(!f(msg))).collect(); + let msgs = self.runtime.block_on(msgs).unwrap(); + + self.receive_messages(msgs); + + &self.messages[old_msg_len..] + } + + /// Blocks until the processing (building + indexing) is done by the RLS. + pub fn wait_for_indexing(&mut self) { + self.take_messages_until(|msg| { + msg["params"]["title"] == "Indexing" && msg["params"]["done"] == true + }); + } + + /// Blocks until RLS responds with a message with a given `id`. + pub fn wait_for_id(&mut self, id: u64) { + self.take_messages_until(|msg| msg["id"] == id); + } + + /// Requests the RLS to shut down and waits (with a timeout) until the child + /// process is terminated. + pub fn shutdown(mut self) { + self.send(json!({"jsonrpc": "2.0", "id": 99999, "method": "shutdown"})); + self.send(json!({"jsonrpc": "2.0", "method": "exit"})); + + let rt = &mut self.runtime; + + let fut = self.child.wait_with_output(); + let fut = Timeout::new(fut, Duration::from_secs(15)); + + rt.block_on(fut).unwrap(); + } +} diff --git a/tests/support/mod.rs b/tests/support/mod.rs index 4d3b3d4aef4..99c1c78a61c 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -21,6 +21,7 @@ use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; +pub mod client; pub mod project_builder; pub mod paths; pub mod harness; From c7190d7f03a9b86dda4393fe23b41e2227733816 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 1 Jan 2019 18:40:16 +0100 Subject: [PATCH 2/3] Use typed requests --- tests/client.rs | 27 ++++++----- tests/support/client.rs | 104 ++++++++++++++++++++++++++++++++-------- 2 files changed, 101 insertions(+), 30 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index 3fe00ecded3..e97300363a1 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -1,5 +1,3 @@ -use serde_json::{self, json}; - use self::support::project_builder::project; #[allow(dead_code)] @@ -86,15 +84,22 @@ fn client_test_simple_workspace() { let root_path = p.root(); let mut rls = p.spawn_rls_async(); - rls.send(json!({ - "jsonrpc": "2.0", - "id": 0, - "method": "initialize", - "params": { - "rootPath": root_path, - "capabilities": {} - } - })); + rls.request::( + 0, + languageserver_types::InitializeParams { + process_id: None, + root_uri: None, + root_path: Some(root_path.display().to_string()), + initialization_options: None, + capabilities: languageserver_types::ClientCapabilities { + workspace: None, + text_document: None, + experimental: None, + }, + trace: None, + workspace_folders: None, + }, + ); rls.wait_for_indexing(); diff --git a/tests/support/client.rs b/tests/support/client.rs index 3fd963c9933..b9e3d15f7f5 100644 --- a/tests/support/client.rs +++ b/tests/support/client.rs @@ -14,6 +14,8 @@ use tokio::runtime::current_thread::Runtime; use tokio_process::{Child, ChildStdin, ChildStdout, CommandExt}; use tokio_timer::Timeout; +use serde::Deserialize; + use super::project_builder::Project; use super::rls_exe; @@ -29,7 +31,10 @@ impl Project { let stdin = child.stdin().take().unwrap(); let stdout = child.stdout().take().unwrap(); - let reader = FramedRead::new(std::io::BufReader::new(stdout), LspDecoder::default()); + let reader = Some(FramedRead::new( + std::io::BufReader::new(stdout), + LspDecoder::default(), + )); let writer = Some(FramedWrite::new(stdin, LspEncoder)); RlsHandle { @@ -46,8 +51,8 @@ impl Project { /// receive messages to and from the process. pub struct RlsHandle { /// Asynchronous LSP reader for the spawned process - reader: FramedRead, LspDecoder>, - /// ASynchronous LSP writer for the spawned process + reader: Option, LspDecoder>>, + /// Asynchronous LSP writer for the spawned process writer: Option>, /// Handle to the spawned child child: Child, @@ -65,54 +70,115 @@ impl RlsHandle { } // TODO: Notify on every message received? - fn receive_messages(&mut self, msgs: Vec) { - for msg in &msgs { - eprintln!("Received: {:?}", msg); - } + fn receive_message(&mut self, msg: Value) { + eprintln!("Received: {:?}", msg); + + self.messages.push(msg); + } + + // TODO: + pub fn request(&mut self, id: u64, params: R::Params) -> R::Result + where + R: rls::lsp_data::LSPRequest, + R::Params: serde::Serialize, + R::Result: serde::de::DeserializeOwned, + { + self.send(json!({ + "jsonrpc": "2.0", + "id": id, + "method": R::METHOD, + "params": params, + })); + + let msg = self.wait_for_message(|val| val["id"] == id && val.get("result").is_some()); + let msg = &msg["result"]; + + R::Result::deserialize(msg).unwrap_or_else(|_| panic!("Can't deserialize results: {:?}", msg)) + } - self.messages.extend(msgs); + pub fn notify(&mut self, params: R::Params) + where + R: rls::lsp_data::LSPNotification, + R::Params: serde::Serialize, + { + self.send(json!({ + "jsonrpc": "2.0", + "method": R::METHOD, + "params": params, + })); } /// Synchronously sends a message to the RLS. pub fn send(&mut self, msg: Value) { let writer = self.writer.take().unwrap(); + eprintln!("Sending: {:?}", msg); let fut = writer.send(msg); self.writer = Some(self.runtime.block_on(fut).unwrap()); + eprintln!("Finished Sending"); } /// Consumes messages in blocking manner until `f` predicate returns true - /// for a received message from the stream. - pub fn take_messages_until(&mut self, f: impl Fn(&Value) -> bool) -> &[Value] { - let stream = self.reader.by_ref(); + /// for a received message from the stream, additionally including the first + /// message for which the predicate returned false. + pub fn take_messages_until_inclusive(&mut self, f: impl Fn(&Value) -> bool) -> &[Value] { + // let stream = self.reader.by_ref(); let old_msg_len = self.messages.len(); - let msgs = stream.take_while(|msg| Ok(!f(msg))).collect(); - let msgs = self.runtime.block_on(msgs).unwrap(); - - self.receive_messages(msgs); + // Fugly workaround to synchronously take items from stream *including* + // the one for which `f` returns false. + // Straightforward implementation of using `by_ref` and then doing + // `take_while(|x| Ok(!f(x))).collect()` doesn't work since it seems + // that the last element for which `f` is false is consumed from the + // inner stream and there's no way to retrieve it afterwards. + loop { + let reader = self.reader.take().unwrap(); + + match self.runtime.block_on(reader.into_future()) { + Ok((item, stream)) => { + if let Some(item) = item { + self.receive_message(item); + } + + self.reader = Some(stream); + }, + Err(..) => panic!("Can't read LSP message from stream"), + } + + let last = self.messages.last().unwrap(); + // *Do* include the last message for which `f` was false. + if f(last) { + break; + } + } &self.messages[old_msg_len..] } + pub fn wait_for_message(&mut self, f: impl Fn(&Value) -> bool) -> &Value { + self.take_messages_until_inclusive(f); + + self.messages.last().unwrap() + } + /// Blocks until the processing (building + indexing) is done by the RLS. pub fn wait_for_indexing(&mut self) { - self.take_messages_until(|msg| { + self.wait_for_message(|msg| { msg["params"]["title"] == "Indexing" && msg["params"]["done"] == true }); } /// Blocks until RLS responds with a message with a given `id`. pub fn wait_for_id(&mut self, id: u64) { - self.take_messages_until(|msg| msg["id"] == id); + self.wait_for_message(|msg| msg["id"] == id); } /// Requests the RLS to shut down and waits (with a timeout) until the child /// process is terminated. pub fn shutdown(mut self) { - self.send(json!({"jsonrpc": "2.0", "id": 99999, "method": "shutdown"})); - self.send(json!({"jsonrpc": "2.0", "method": "exit"})); + self.request::(99999, ()); + self.notify::(()); let rt = &mut self.runtime; From 9148b8a81412c3af31fa5e9b4833b25bd163a7a5 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Wed, 2 Jan 2019 15:24:35 +0100 Subject: [PATCH 3/3] Implement asynchronous message reading --- tests/support/client.rs | 196 +++++++++++++++++++++++----------------- 1 file changed, 112 insertions(+), 84 deletions(-) diff --git a/tests/support/client.rs b/tests/support/client.rs index b9e3d15f7f5..3f7fb47121e 100644 --- a/tests/support/client.rs +++ b/tests/support/client.rs @@ -1,23 +1,41 @@ //! Tokio-based LSP client. The tokio `current_thread::Runtime` allows for a //! cheap, single-threaded blocking until a certain message is received from the //! server. It also allows enforcing timeouts, which are necessary for testing. - +//! +//! More concretely, we couple spawned RLS handle with the Tokio runtime on +//! current thread. A message reader `Stream` future is +//! spawned on the runtime, which allows us to queue channel senders which can +//! be notified when resolving the reader future. On each message reception we +//! check if a channel sender was registered with an associated predicate being +//! true for the message received, and if so we send the message, notifying the +//! receiver (thus, implementing the Future model). + +use std::cell::{Ref, RefCell}; use std::process::{Command, Stdio}; -use std::time::Duration; +use std::rc::Rc; use futures::sink::Sink; use futures::stream::Stream; +use futures::unsync::oneshot; +use futures::Future; use lsp_codec::{LspDecoder, LspEncoder}; +use serde::Deserialize; use serde_json::{json, Value}; use tokio::codec::{FramedRead, FramedWrite}; use tokio::runtime::current_thread::Runtime; -use tokio_process::{Child, ChildStdin, ChildStdout, CommandExt}; -use tokio_timer::Timeout; - -use serde::Deserialize; +use tokio::util::FutureExt; +use tokio_process::{Child, ChildStdin, CommandExt}; use super::project_builder::Project; -use super::rls_exe; +use super::{rls_exe, rls_timeout}; + +// `Rc` because we share those in message reader stream and the RlsHandle. +// `RefCell` because borrows don't overlap. This is safe, because `process_msg` +// is only called (synchronously) when we execute some work on the runtime, +// however we only call `Runtime::block_on` and whenever we do it, there are no +// active borrows in scope. +type Messages = Rc>>; +type Channels = Rc bool>, oneshot::Sender)>>>; impl Project { pub fn spawn_rls_async(&self) -> RlsHandle { @@ -31,52 +49,93 @@ impl Project { let stdin = child.stdin().take().unwrap(); let stdout = child.stdout().take().unwrap(); - let reader = Some(FramedRead::new( - std::io::BufReader::new(stdout), - LspDecoder::default(), - )); + let msgs = Messages::default(); + let chans = Channels::default(); + + let reader = FramedRead::new(std::io::BufReader::new(stdout), LspDecoder::default()) + .map_err(|_| ()) + .for_each({ + let msgs = Rc::clone(&msgs); + let chans = Rc::clone(&chans); + move |msg| process_msg(msg, msgs.clone(), chans.clone()) + }) + .timeout(rls_timeout()); + let writer = Some(FramedWrite::new(stdin, LspEncoder)); + let mut rt = Runtime::new().unwrap(); + rt.spawn(reader.map_err(|_| ())); + RlsHandle { - reader, writer, child, - runtime: Runtime::new().unwrap(), - messages: Vec::new(), + runtime: rt, + messages: msgs, + channels: chans, } } } +fn process_msg(msg: Value, msgs: Messages, chans: Channels) -> Result<(), ()> { + eprintln!("Processing message: {:?}", msg); + + let mut chans = chans.borrow_mut(); + + if chans.len() > 0 { + let mut idx = (chans.len() - 1) as isize; + + // Poor man's drain_filter. Iterates over entire collection starting + // from the end, takes ownership over the element and the predicate is + // true, then we consume the value; otherwise, we push it to the back, + // effectively undoing swap_remove (post-swap). This is correct, because + // on every iteration we decrease idx by 1, so we won't loop and we will + // check every element. + while idx >= 0 { + let (pred, tx) = chans.swap_remove(idx as usize); + if pred(&msg) { + tx.send(msg.clone()).map_err(|_| ())?; + } else { + chans.push((pred, tx)); + } + + idx -= 1; + } + + debug_assert!(chans.iter().all(|(pred, _)| !pred(&msg))); + } + + msgs.borrow_mut().push(msg); + + Ok(()) +} + /// Holds the handle to a spawned RLS child process and allows to send and /// receive messages to and from the process. pub struct RlsHandle { - /// Asynchronous LSP reader for the spawned process - reader: Option, LspDecoder>>, - /// Asynchronous LSP writer for the spawned process + /// Asynchronous LSP writer for the spawned process. writer: Option>, - /// Handle to the spawned child + /// Handle to the spawned child. child: Child, - /// Tokio single-thread runtime required for interaction with async-based - /// `reader` and `writer` + /// Tokio single-thread runtime onto which LSP message reading stream has + /// been spawned. Allows to synchronously write messages via `writer` and + /// block on received messages matching an enqueued predicate in `channels`. runtime: Runtime, - /// LSP Messages received from the stream and processed - messages: Vec, + /// Handle to all of the received LSP messages. + messages: Messages, + /// Handle to enqueued channel senders, used to notify when a given message + /// has been received. + channels: Channels, } impl RlsHandle { /// Returns messages received until the moment of the call. - pub fn messages(&self) -> &[Value] { - &self.messages - } - - // TODO: Notify on every message received? - fn receive_message(&mut self, msg: Value) { - eprintln!("Received: {:?}", msg); - - self.messages.push(msg); + pub fn messages(&self) -> Ref> { + self.messages.borrow() } - // TODO: + /// Send a request to the RLS and block until we receive the message. + /// Note that between sending and receiving the response *another* messages + /// can be received. pub fn request(&mut self, id: u64, params: R::Params) -> R::Result where R: rls::lsp_data::LSPRequest, @@ -90,12 +149,13 @@ impl RlsHandle { "params": params, })); - let msg = self.wait_for_message(|val| val["id"] == id && val.get("result").is_some()); - let msg = &msg["result"]; + let msg = self.wait_for_message(move |val| val["id"] == id && val.get("result").is_some()); - R::Result::deserialize(msg).unwrap_or_else(|_| panic!("Can't deserialize results: {:?}", msg)) + R::Result::deserialize(&msg["result"]) + .unwrap_or_else(|_| panic!("Can't deserialize results: {:?}", msg)) } + /// Synchronously sends a notification to the RLS. pub fn notify(&mut self, params: R::Params) where R: rls::lsp_data::LSPNotification, @@ -110,56 +170,32 @@ impl RlsHandle { /// Synchronously sends a message to the RLS. pub fn send(&mut self, msg: Value) { + eprintln!("Sending: {:?}", msg); + let writer = self.writer.take().unwrap(); - eprintln!("Sending: {:?}", msg); let fut = writer.send(msg); self.writer = Some(self.runtime.block_on(fut).unwrap()); - eprintln!("Finished Sending"); } - /// Consumes messages in blocking manner until `f` predicate returns true - /// for a received message from the stream, additionally including the first - /// message for which the predicate returned false. - pub fn take_messages_until_inclusive(&mut self, f: impl Fn(&Value) -> bool) -> &[Value] { - // let stream = self.reader.by_ref(); - let old_msg_len = self.messages.len(); - - // Fugly workaround to synchronously take items from stream *including* - // the one for which `f` returns false. - // Straightforward implementation of using `by_ref` and then doing - // `take_while(|x| Ok(!f(x))).collect()` doesn't work since it seems - // that the last element for which `f` is false is consumed from the - // inner stream and there's no way to retrieve it afterwards. - loop { - let reader = self.reader.take().unwrap(); - - match self.runtime.block_on(reader.into_future()) { - Ok((item, stream)) => { - if let Some(item) = item { - self.receive_message(item); - } - - self.reader = Some(stream); - }, - Err(..) => panic!("Can't read LSP message from stream"), - } + /// Enqueues a channel that is notified and consumed when a given predicate + /// `f` is true for a received message. + fn future_msg( + &mut self, + f: impl Fn(&Value) -> bool + 'static, + ) -> impl Future { + let (tx, rx) = oneshot::channel(); - let last = self.messages.last().unwrap(); - // *Do* include the last message for which `f` was false. - if f(last) { - break; - } - } + self.channels.borrow_mut().push((Box::new(f), tx)); - &self.messages[old_msg_len..] + rx } - pub fn wait_for_message(&mut self, f: impl Fn(&Value) -> bool) -> &Value { - self.take_messages_until_inclusive(f); - - self.messages.last().unwrap() + /// Blocks until a message, for which predicate `f` returns true, is received. + pub fn wait_for_message(&mut self, f: impl Fn(&Value) -> bool + 'static) -> Value { + let fut = self.future_msg(f); + self.runtime.block_on(fut).unwrap() } /// Blocks until the processing (building + indexing) is done by the RLS. @@ -169,22 +205,14 @@ impl RlsHandle { }); } - /// Blocks until RLS responds with a message with a given `id`. - pub fn wait_for_id(&mut self, id: u64) { - self.wait_for_message(|msg| msg["id"] == id); - } - /// Requests the RLS to shut down and waits (with a timeout) until the child /// process is terminated. pub fn shutdown(mut self) { self.request::(99999, ()); self.notify::(()); - let rt = &mut self.runtime; - - let fut = self.child.wait_with_output(); - let fut = Timeout::new(fut, Duration::from_secs(15)); + let fut = self.child.wait_with_output().timeout(rls_timeout()); - rt.block_on(fut).unwrap(); + self.runtime.block_on(fut).unwrap(); } }