Skip to content
This repository was archived by the owner on Dec 29, 2022. It is now read-only.

Commit b3126a8

Browse files
authored
Merge pull request #654 from Xanewok/int_tests2
Add integration tests based on #607
2 parents c6c6aca + cf82186 commit b3126a8

File tree

3 files changed

+608
-0
lines changed

3 files changed

+608
-0
lines changed

tests/support/mod.rs

Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use serde_json;
12+
use std::env;
13+
use std::fs;
14+
use std::io::{self, BufRead, BufReader, Read, Write};
15+
use std::mem;
16+
use std::path::{Path, PathBuf};
17+
use std::process::{Child, ChildStdin, ChildStdout, Command, Stdio};
18+
use std::str;
19+
use std::sync::{Arc, Condvar, Mutex};
20+
use std::thread;
21+
use std::time::Duration;
22+
23+
24+
use support::paths::TestPathExt;
25+
26+
pub mod paths;
27+
28+
/// Executes `func` and panics if it takes longer than `dur`.
29+
pub fn timeout<F>(dur: Duration, func: F)
30+
where F: FnOnce() + Send + 'static {
31+
let pair = Arc::new((Mutex::new(false), Condvar::new()));
32+
let pair2 = pair.clone();
33+
34+
thread::spawn(move|| {
35+
let &(ref lock, ref cvar) = &*pair2;
36+
func();
37+
let mut finished = lock.lock().unwrap();
38+
*finished = true;
39+
// We notify the condvar that the value has changed.
40+
cvar.notify_one();
41+
});
42+
43+
// Wait for the test to finish.
44+
let &(ref lock, ref cvar) = &*pair;
45+
let mut finished = lock.lock().unwrap();
46+
// As long as the value inside the `Mutex` is false, we wait.
47+
while !*finished {
48+
let result = cvar.wait_timeout(finished, dur).unwrap();
49+
if result.1.timed_out() {
50+
panic!("Timed out")
51+
}
52+
finished = result.0
53+
}
54+
}
55+
56+
#[derive(Clone, Debug)]
57+
pub struct ExpectedMessage {
58+
id: Option<u64>,
59+
contains: Vec<String>,
60+
}
61+
62+
impl ExpectedMessage {
63+
pub fn new(id: Option<u64>) -> ExpectedMessage {
64+
ExpectedMessage {
65+
id: id,
66+
contains: vec![],
67+
}
68+
}
69+
70+
pub fn expect_contains(&mut self, s: &str) -> &mut ExpectedMessage {
71+
self.contains.push(s.to_owned());
72+
self
73+
}
74+
}
75+
76+
pub fn read_message<R: Read>(reader: &mut BufReader<R>) -> io::Result<String> {
77+
let mut content_length = None;
78+
// Read the headers
79+
loop {
80+
let mut header = String::new();
81+
reader.read_line(&mut header)?;
82+
if header.len() == 0 {
83+
panic!("eof")
84+
}
85+
if header == "\r\n" {
86+
// This is the end of the headers
87+
break;
88+
}
89+
let parts: Vec<&str> = header.splitn(2, ": ").collect();
90+
if parts[0] == "Content-Length" {
91+
content_length = Some(parts[1].trim().parse::<usize>().unwrap())
92+
}
93+
}
94+
95+
// Read the actual message
96+
let content_length = content_length.expect("did not receive Content-Length header");
97+
let mut msg = vec![0; content_length];
98+
reader.read_exact(&mut msg)?;
99+
let result = String::from_utf8_lossy(&msg).into_owned();
100+
Ok(result)
101+
}
102+
103+
pub fn expect_messages<R: Read>(reader: &mut BufReader<R>, expected: &[&ExpectedMessage]) {
104+
let mut results: Vec<String> = Vec::new();
105+
while results.len() < expected.len() {
106+
let msg = read_message(reader).unwrap();
107+
results.push(msg);
108+
}
109+
110+
println!(
111+
"expect_messages:\n results: {:#?},\n expected: {:#?}",
112+
results,
113+
expected
114+
);
115+
assert_eq!(results.len(), expected.len());
116+
for (found, expected) in results.iter().zip(expected.iter()) {
117+
let values: serde_json::Value = serde_json::from_str(found).unwrap();
118+
assert!(
119+
values
120+
.get("jsonrpc")
121+
.expect("Missing jsonrpc field")
122+
.as_str()
123+
.unwrap() == "2.0",
124+
"Bad jsonrpc field"
125+
);
126+
if let Some(id) = expected.id {
127+
assert_eq!(
128+
values
129+
.get("id")
130+
.expect("Missing id field")
131+
.as_u64()
132+
.unwrap(),
133+
id,
134+
"Unexpected id"
135+
);
136+
}
137+
for c in expected.contains.iter() {
138+
found
139+
.find(c)
140+
.expect(&format!("Could not find `{}` in `{}`", c, found));
141+
}
142+
}
143+
}
144+
145+
pub struct RlsHandle {
146+
child: Child,
147+
stdin: ChildStdin,
148+
stdout: BufReader<ChildStdout>,
149+
}
150+
151+
impl RlsHandle {
152+
pub fn new(mut child: Child) -> RlsHandle {
153+
let stdin = mem::replace(&mut child.stdin, None).unwrap();
154+
let stdout = mem::replace(&mut child.stdout, None).unwrap();
155+
let stdout = BufReader::new(stdout);
156+
157+
RlsHandle {
158+
child,
159+
stdin,
160+
stdout,
161+
}
162+
}
163+
164+
pub fn send_string(&mut self, s: &str) -> io::Result<usize> {
165+
let full_msg = format!("Content-Length: {}\r\n\r\n{}", s.len(), s);
166+
self.stdin.write(full_msg.as_bytes())
167+
}
168+
pub fn send(&mut self, j: serde_json::Value) -> io::Result<usize> {
169+
self.send_string(&j.to_string())
170+
}
171+
pub fn notify(&mut self, method: &str, params: serde_json::Value) -> io::Result<usize> {
172+
self.send(json!({
173+
"jsonrpc": "2.0",
174+
"method": method,
175+
"params": params,
176+
}))
177+
}
178+
pub fn request(&mut self, id: u64, method: &str, params: serde_json::Value) -> io::Result<usize> {
179+
self.send(json!({
180+
"jsonrpc": "2.0",
181+
"id": id,
182+
"method": method,
183+
"params": params,
184+
}))
185+
}
186+
pub fn shutdown_exit(&mut self) {
187+
self.request(99999, "shutdown", json!({})).unwrap();
188+
189+
self.expect_messages(&[
190+
&ExpectedMessage::new(Some(99999)),
191+
]);
192+
193+
self.notify("exit", json!({})).unwrap();
194+
195+
let ecode = self.child.wait()
196+
.expect("failed to wait on child rls process");
197+
198+
assert!(ecode.success());
199+
}
200+
201+
pub fn expect_messages(&mut self, expected: &[&ExpectedMessage]) {
202+
expect_messages(&mut self.stdout, expected);
203+
}
204+
}
205+
206+
#[derive(PartialEq,Clone)]
207+
struct FileBuilder {
208+
path: PathBuf,
209+
body: String
210+
}
211+
212+
impl FileBuilder {
213+
pub fn new(path: PathBuf, body: &str) -> FileBuilder {
214+
FileBuilder { path: path, body: body.to_string() }
215+
}
216+
217+
fn mk(&self) {
218+
self.dirname().mkdir_p();
219+
220+
let mut file = fs::File::create(&self.path).unwrap_or_else(|e| {
221+
panic!("could not create file {}: {}", self.path.display(), e)
222+
});
223+
224+
file.write_all(self.body.as_bytes()).unwrap();
225+
}
226+
227+
fn dirname(&self) -> &Path {
228+
self.path.parent().unwrap()
229+
}
230+
}
231+
232+
#[derive(PartialEq,Clone)]
233+
pub struct Project{
234+
root: PathBuf,
235+
}
236+
237+
#[must_use]
238+
#[derive(PartialEq,Clone)]
239+
pub struct ProjectBuilder {
240+
name: String,
241+
root: Project,
242+
files: Vec<FileBuilder>,
243+
}
244+
245+
impl ProjectBuilder {
246+
pub fn new(name: &str, root: PathBuf) -> ProjectBuilder {
247+
ProjectBuilder {
248+
name: name.to_string(),
249+
root: Project{ root },
250+
files: vec![],
251+
}
252+
}
253+
254+
pub fn file<B: AsRef<Path>>(mut self, path: B,
255+
body: &str) -> Self {
256+
self._file(path.as_ref(), body);
257+
self
258+
}
259+
260+
fn _file(&mut self, path: &Path, body: &str) {
261+
self.files.push(FileBuilder::new(self.root.root.join(path), body));
262+
}
263+
264+
pub fn build(self) -> Project {
265+
// First, clean the directory if it already exists
266+
self.rm_root();
267+
268+
// Create the empty directory
269+
self.root.root.mkdir_p();
270+
271+
for file in self.files.iter() {
272+
file.mk();
273+
}
274+
275+
self.root
276+
}
277+
278+
fn rm_root(&self) {
279+
self.root.root.rm_rf()
280+
}
281+
}
282+
283+
impl Project {
284+
pub fn root(&self) -> PathBuf {
285+
self.root.clone()
286+
}
287+
288+
pub fn rls(&self) -> Command {
289+
let mut cmd = Command::new(rls_exe());
290+
cmd.stdin(Stdio::piped())
291+
.stdout(Stdio::piped())
292+
.current_dir(self.root());
293+
cmd
294+
}
295+
}
296+
297+
// Generates a project layout
298+
pub fn project(name: &str) -> ProjectBuilder {
299+
ProjectBuilder::new(name, paths::root().join(name))
300+
}
301+
302+
// Path to cargo executables
303+
pub fn target_conf_dir() -> PathBuf {
304+
let mut path = env::current_exe().unwrap();
305+
path.pop();
306+
if path.ends_with("deps") {
307+
path.pop();
308+
}
309+
path
310+
}
311+
312+
pub fn rls_exe() -> PathBuf {
313+
target_conf_dir().join(format!("rls{}", env::consts::EXE_SUFFIX))
314+
}
315+
316+
#[allow(dead_code)]
317+
pub fn main_file(println: &str, deps: &[&str]) -> String {
318+
let mut buf = String::new();
319+
320+
for dep in deps.iter() {
321+
buf.push_str(&format!("extern crate {};\n", dep));
322+
}
323+
324+
buf.push_str("fn main() { println!(");
325+
buf.push_str(&println);
326+
buf.push_str("); }\n");
327+
328+
buf.to_string()
329+
}
330+
331+
pub fn basic_bin_manifest(name: &str) -> String {
332+
format!(r#"
333+
[package]
334+
name = "{}"
335+
version = "0.5.0"
336+
authors = ["[email protected]"]
337+
[[bin]]
338+
name = "{}"
339+
"#, name, name)
340+
}
341+
342+
#[allow(dead_code)]
343+
pub fn basic_lib_manifest(name: &str) -> String {
344+
format!(r#"
345+
[package]
346+
name = "{}"
347+
version = "0.5.0"
348+
authors = ["[email protected]"]
349+
[lib]
350+
name = "{}"
351+
"#, name, name)
352+
}

0 commit comments

Comments
 (0)