This repository was archived by the owner on Dec 29, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 255
Added integration tests, added back test_simple_workspace #607
Merged
Merged
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
use std::io::{self, BufRead, BufReader, Read, Write}; | ||
use std::mem; | ||
use std::process::{Child, ChildStdin, ChildStdout}; | ||
|
||
use serde_json; | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct ExpectedMessage { | ||
id: Option<u64>, | ||
contains: Vec<String>, | ||
} | ||
|
||
impl ExpectedMessage { | ||
pub fn new(id: Option<u64>) -> ExpectedMessage { | ||
ExpectedMessage { | ||
id: id, | ||
contains: vec![], | ||
} | ||
} | ||
|
||
pub fn expect_contains(&mut self, s: &str) -> &mut ExpectedMessage { | ||
self.contains.push(s.to_owned()); | ||
self | ||
} | ||
} | ||
|
||
pub fn read_message<R: Read>(reader: &mut BufReader<R>) -> io::Result<String> { | ||
let mut content_length = None; | ||
// Read the headers | ||
loop { | ||
let mut header = String::new(); | ||
reader.read_line(&mut header)?; | ||
if header.len() == 0 { | ||
panic!("eof") | ||
} | ||
if header == "\r\n" { | ||
// This is the end of the headers | ||
break; | ||
} | ||
let parts: Vec<&str> = header.splitn(2, ": ").collect(); | ||
if parts[0] == "Content-Length" { | ||
content_length = Some(parts[1].trim().parse::<usize>().unwrap()) | ||
} | ||
} | ||
|
||
// Read the actual message | ||
let content_length = content_length.expect("did not receive Content-Length header"); | ||
let mut msg = vec![0; content_length]; | ||
reader.read_exact(&mut msg)?; | ||
let result = String::from_utf8_lossy(&msg).into_owned(); | ||
Ok(result) | ||
} | ||
|
||
pub fn expect_messages<R: Read>(reader: &mut BufReader<R>, expected: &[&ExpectedMessage]) { | ||
let mut results: Vec<String> = Vec::new(); | ||
while results.len() < expected.len() { | ||
results.push(read_message(reader).unwrap()); | ||
} | ||
|
||
println!( | ||
"expect_messages:\n results: {:#?},\n expected: {:#?}", | ||
results, | ||
expected | ||
); | ||
assert_eq!(results.len(), expected.len()); | ||
for (found, expected) in results.iter().zip(expected.iter()) { | ||
let values: serde_json::Value = serde_json::from_str(found).unwrap(); | ||
assert!( | ||
values | ||
.get("jsonrpc") | ||
.expect("Missing jsonrpc field") | ||
.as_str() | ||
.unwrap() == "2.0", | ||
"Bad jsonrpc field" | ||
); | ||
if let Some(id) = expected.id { | ||
assert_eq!( | ||
values | ||
.get("id") | ||
.expect("Missing id field") | ||
.as_u64() | ||
.unwrap(), | ||
id, | ||
"Unexpected id" | ||
); | ||
} | ||
for c in expected.contains.iter() { | ||
found | ||
.find(c) | ||
.expect(&format!("Could not find `{}` in `{}`", c, found)); | ||
} | ||
} | ||
} | ||
|
||
pub struct RlsHandle { | ||
child: Child, | ||
stdin: ChildStdin, | ||
stdout: BufReader<ChildStdout>, | ||
} | ||
|
||
impl RlsHandle { | ||
pub fn new(mut child: Child) -> RlsHandle { | ||
let stdin = mem::replace(&mut child.stdin, None).unwrap(); | ||
let stdout = mem::replace(&mut child.stdout, None).unwrap(); | ||
let stdout = BufReader::new(stdout); | ||
|
||
RlsHandle { | ||
child, | ||
stdin, | ||
stdout, | ||
} | ||
} | ||
|
||
pub fn send_string(&mut self, s: &str) -> io::Result<usize> { | ||
let full_msg = format!("Content-Length: {}\r\n\r\n{}", s.len(), s); | ||
self.stdin.write(full_msg.as_bytes()) | ||
} | ||
pub fn send(&mut self, j: serde_json::Value) -> io::Result<usize> { | ||
self.send_string(&j.to_string()) | ||
} | ||
pub fn notify(&mut self, method: &str, params: serde_json::Value) -> io::Result<usize> { | ||
self.send(json!({ | ||
"jsonrpc": "2.0", | ||
"method": method, | ||
"params": params, | ||
})) | ||
} | ||
pub fn request(&mut self, id: u64, method: &str, params: serde_json::Value) -> io::Result<usize> { | ||
self.send(json!({ | ||
"jsonrpc": "2.0", | ||
"id": id, | ||
"method": method, | ||
"params": params, | ||
})) | ||
} | ||
pub fn shutdown_exit(&mut self) { | ||
self.request(99999, "shutdown", json!({})).unwrap(); | ||
|
||
self.expect_messages(&[ | ||
&ExpectedMessage::new(Some(99999)), | ||
]); | ||
|
||
self.notify("exit", json!({})).unwrap(); | ||
|
||
let ecode = self.child.wait() | ||
.expect("failed to wait on child rls process"); | ||
|
||
assert!(ecode.success()); | ||
} | ||
|
||
pub fn expect_messages(&mut self, expected: &[&ExpectedMessage]) { | ||
expect_messages(&mut self.stdout, expected); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
use std::env; | ||
use std::fs; | ||
use std::io::prelude::*; | ||
use std::path::{Path, PathBuf}; | ||
use std::process::{Command, Stdio}; | ||
use std::str; | ||
|
||
use support::paths::TestPathExt; | ||
|
||
pub mod harness; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are some things here and some things in |
||
pub mod paths; | ||
|
||
#[derive(PartialEq,Clone)] | ||
struct FileBuilder { | ||
path: PathBuf, | ||
body: String | ||
} | ||
|
||
impl FileBuilder { | ||
pub fn new(path: PathBuf, body: &str) -> FileBuilder { | ||
FileBuilder { path: path, body: body.to_string() } | ||
} | ||
|
||
fn mk(&self) { | ||
self.dirname().mkdir_p(); | ||
|
||
let mut file = fs::File::create(&self.path).unwrap_or_else(|e| { | ||
panic!("could not create file {}: {}", self.path.display(), e) | ||
}); | ||
|
||
file.write_all(self.body.as_bytes()).unwrap(); | ||
} | ||
|
||
fn dirname(&self) -> &Path { | ||
self.path.parent().unwrap() | ||
} | ||
} | ||
|
||
#[derive(PartialEq,Clone)] | ||
pub struct Project{ | ||
root: PathBuf, | ||
} | ||
|
||
#[must_use] | ||
#[derive(PartialEq,Clone)] | ||
pub struct ProjectBuilder { | ||
name: String, | ||
root: Project, | ||
files: Vec<FileBuilder>, | ||
} | ||
|
||
impl ProjectBuilder { | ||
pub fn new(name: &str, root: PathBuf) -> ProjectBuilder { | ||
ProjectBuilder { | ||
name: name.to_string(), | ||
root: Project{ root }, | ||
files: vec![], | ||
} | ||
} | ||
|
||
pub fn file<B: AsRef<Path>>(mut self, path: B, | ||
body: &str) -> Self { | ||
self._file(path.as_ref(), body); | ||
self | ||
} | ||
|
||
fn _file(&mut self, path: &Path, body: &str) { | ||
self.files.push(FileBuilder::new(self.root.root.join(path), body)); | ||
} | ||
|
||
pub fn build(self) -> Project { | ||
// First, clean the directory if it already exists | ||
self.rm_root(); | ||
|
||
// Create the empty directory | ||
self.root.root.mkdir_p(); | ||
|
||
for file in self.files.iter() { | ||
file.mk(); | ||
} | ||
|
||
self.root | ||
} | ||
|
||
fn rm_root(&self) { | ||
self.root.root.rm_rf() | ||
} | ||
} | ||
|
||
impl Project { | ||
pub fn root(&self) -> PathBuf { | ||
self.root.clone() | ||
} | ||
|
||
pub fn rls(&self) -> Command { | ||
let mut cmd = Command::new(rls_exe()); | ||
cmd.stdin(Stdio::piped()) | ||
.stdout(Stdio::piped()) | ||
.current_dir(self.root()); | ||
cmd | ||
} | ||
} | ||
|
||
// Generates a project layout | ||
pub fn project(name: &str) -> ProjectBuilder { | ||
ProjectBuilder::new(name, paths::root().join(name)) | ||
} | ||
|
||
// Path to cargo executables | ||
pub fn target_conf_dir() -> PathBuf { | ||
let mut path = env::current_exe().unwrap(); | ||
path.pop(); | ||
if path.ends_with("deps") { | ||
path.pop(); | ||
} | ||
path | ||
} | ||
|
||
pub fn rls_exe() -> PathBuf { | ||
target_conf_dir().join(format!("rls{}", env::consts::EXE_SUFFIX)) | ||
} | ||
|
||
pub fn main_file(println: &str, deps: &[&str]) -> String { | ||
let mut buf = String::new(); | ||
|
||
for dep in deps.iter() { | ||
buf.push_str(&format!("extern crate {};\n", dep)); | ||
} | ||
|
||
buf.push_str("fn main() { println!("); | ||
buf.push_str(&println); | ||
buf.push_str("); }\n"); | ||
|
||
buf.to_string() | ||
} | ||
|
||
pub fn basic_bin_manifest(name: &str) -> String { | ||
format!(r#" | ||
[package] | ||
name = "{}" | ||
version = "0.5.0" | ||
authors = ["[email protected]"] | ||
[[bin]] | ||
name = "{}" | ||
"#, name, name) | ||
} | ||
|
||
pub fn basic_lib_manifest(name: &str) -> String { | ||
format!(r#" | ||
[package] | ||
name = "{}" | ||
version = "0.5.0" | ||
authors = ["[email protected]"] | ||
[lib] | ||
name = "{}" | ||
"#, name, name) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this buffered? So we can easily only read a message when a whole message is ready to be read?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that I can search for a \n using read_line when reading the headers like Content-Length: XX