Skip to content
This repository was archived by the owner on Nov 24, 2023. It is now read-only.

Commit 17507cf

Browse files
authored
Merge pull request #44 from killercup/refactor/tests
Refactor tests to use explicit fixtures
2 parents 2ef7bcc + 3c61332 commit 17507cf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+273
-900
lines changed

.gitmodules

-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +0,0 @@
1-
[submodule "tests/fixtures/libui-rs"]
2-
path = tests/crates/libui-rs
3-
url = https://github.com/pcwalton/libui-rs.git

.travis.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ language: rust
22
rust:
33
- nightly
44
- stable
5-
5+
before_script:
6+
- if [ $TRAVIS_RUST_VERSION == nightly ]; then cargo install clippy --force ; fi
67
script:
78
- cargo build
89
- if [ $TRAVIS_RUST_VERSION == nightly ]; then cargo test -- --nocapture ; fi

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,7 @@ serde_derive = "1.0"
2626

2727
[dev-dependencies]
2828
duct = "0.8.2"
29+
env_logger = "0.5.0-rc.1"
30+
log = "0.4.1"
31+
pretty_assertions = "0.4.1"
32+
tempdir = "0.3.5"

src/lib.rs

+14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ use std::collections::HashSet;
77
pub mod diagnostics;
88
use diagnostics::{Diagnostic, DiagnosticSpan};
99

10+
pub fn get_suggestions_from_json(input: &str, only: &HashSet<String>) -> Vec<Suggestion> {
11+
input.lines()
12+
.filter(not_empty)
13+
// Convert JSON string (and eat parsing errors)
14+
.flat_map(|line| serde_json::from_str::<Diagnostic>(line))
15+
// One diagnostic line might have multiple suggestions
16+
.filter_map(|cargo_msg| collect_suggestions(&cargo_msg, only))
17+
.collect()
18+
}
19+
1020
#[derive(Debug, Copy, Clone, Hash, PartialEq)]
1121
pub struct LinePosition {
1222
pub line: usize,
@@ -153,3 +163,7 @@ pub fn collect_suggestions(diagnostic: &Diagnostic, only: &HashSet<String>) -> O
153163
})
154164
}
155165
}
166+
167+
fn not_empty(s: &&str) -> bool {
168+
!s.trim().is_empty()
169+
}

tests/crates/libui-rs

-1
This file was deleted.

tests/crates/libui-rs.sub-path

-1
This file was deleted.

tests/crates/use_test/Cargo.toml

-6
This file was deleted.

tests/crates/use_test/src/lib.rs

-3
This file was deleted.

tests/everything.rs

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#[macro_use] extern crate duct;
2+
#[macro_use] extern crate pretty_assertions;
3+
extern crate tempdir;
4+
#[macro_use] extern crate log;
5+
extern crate env_logger;
6+
extern crate serde_json;
7+
extern crate rustfix;
8+
9+
use std::fs;
10+
use std::error::Error;
11+
use std::path::{Path, PathBuf};
12+
use std::collections::HashSet;
13+
use tempdir::TempDir;
14+
15+
use rustfix::Replacement;
16+
17+
fn compile_and_get_json_errors(file: &Path) -> Result<String, Box<Error>> {
18+
let tmp = TempDir::new("rustfix-tests")?;
19+
let better_call_clippy = cmd!(
20+
"clippy-driver", "rustc", file,
21+
"--error-format=json", "--emit=metadata",
22+
"--out-dir", tmp.path()
23+
);
24+
let res = better_call_clippy
25+
.stdout_capture()
26+
.stderr_capture()
27+
.unchecked()
28+
.run()?;
29+
let stderr = String::from_utf8(res.stderr)?;
30+
31+
use std::io::{Error, ErrorKind};
32+
match res.status.code() {
33+
Some(0) | Some(1) => Ok(stderr),
34+
_ => Err(Box::new(Error::new(
35+
ErrorKind::Other,
36+
format!("failed with status {:?}: {}", res.status.code(), stderr),
37+
)))
38+
}
39+
}
40+
41+
fn read_file(path: &Path) -> Result<String, Box<Error>> {
42+
use std::io::Read;
43+
44+
let mut buffer = String::new();
45+
let mut file = fs::File::open(path)?;
46+
file.read_to_string(&mut buffer)?;
47+
Ok(buffer)
48+
}
49+
50+
fn apply_suggestion(file_content: &mut String, suggestion: &Replacement) -> Result<String, Box<Error>> {
51+
use std::cmp::max;
52+
53+
let mut new_content = String::new();
54+
55+
// Add the lines before the section we want to replace
56+
new_content.push_str(&file_content.lines()
57+
.take(max(suggestion.snippet.line_range.start.line - 1, 0) as usize)
58+
.collect::<Vec<_>>()
59+
.join("\n"));
60+
new_content.push_str("\n");
61+
62+
// Parts of line before replacement
63+
new_content.push_str(&file_content.lines()
64+
.nth(suggestion.snippet.line_range.start.line - 1)
65+
.unwrap_or("")
66+
.chars()
67+
.take(suggestion.snippet.line_range.start.column - 1)
68+
.collect::<String>());
69+
70+
// Insert new content! Finally!
71+
new_content.push_str(&suggestion.replacement);
72+
73+
// Parts of line after replacement
74+
new_content.push_str(&file_content.lines()
75+
.nth(suggestion.snippet.line_range.end.line - 1)
76+
.unwrap_or("")
77+
.chars()
78+
.skip(suggestion.snippet.line_range.end.column - 1)
79+
.collect::<String>());
80+
81+
// Add the lines after the section we want to replace
82+
new_content.push_str("\n");
83+
new_content.push_str(&file_content.lines()
84+
.skip(suggestion.snippet.line_range.end.line as usize)
85+
.collect::<Vec<_>>()
86+
.join("\n"));
87+
new_content.push_str("\n");
88+
89+
Ok(new_content)
90+
}
91+
92+
fn test_rustfix_with_file<P: AsRef<Path>>(file: P) -> Result<(), Box<Error>> {
93+
let file: &Path = file.as_ref();
94+
debug!("{:?}", file);
95+
let code = read_file(&file)?;
96+
let errors = compile_and_get_json_errors(file)?;
97+
98+
if std::env::var("RUSTFIX_TEST_RECORD_JSON").is_ok() {
99+
use std::io::Write;
100+
let mut recorded_json = fs::File::create(&file.with_extension("recorded.json"))?;
101+
recorded_json.write_all(errors.as_bytes())?;
102+
}
103+
104+
let expected_json = read_file(&file.with_extension("json"))?;
105+
106+
assert_eq!(
107+
errors.trim(),
108+
expected_json.trim(),
109+
"got unexpected json from clippy"
110+
);
111+
112+
let suggestions = rustfix::get_suggestions_from_json(&errors, &HashSet::new());
113+
let mut fixed = code.clone();
114+
115+
for sug in suggestions {
116+
trace!("{:?}", sug);
117+
for sol in sug.solutions {
118+
trace!("{:?}", sol);
119+
for r in sol.replacements {
120+
info!("replaced.");
121+
trace!("{:?}", r);
122+
fixed = apply_suggestion(&mut fixed, &r)?;
123+
}
124+
}
125+
}
126+
127+
let expected_fixed = read_file(&file.with_extension("fixed.rs"))?;
128+
assert_eq!(fixed.trim(), expected_fixed.trim(), "file doesn't look fixed");
129+
Ok(())
130+
}
131+
132+
fn get_fixture_files() -> Result<Vec<PathBuf>, Box<Error>> {
133+
Ok(fs::read_dir("./tests/fixtures")?
134+
.into_iter()
135+
.map(|e| e.unwrap().path())
136+
.filter(|p| p.is_file())
137+
.filter(|p| {
138+
let x = p.to_string_lossy();
139+
x.ends_with(".rs") && !x.ends_with(".fixed.rs")
140+
})
141+
.collect())
142+
}
143+
144+
#[test]
145+
fn fixtures() {
146+
let _ = env_logger::try_init();
147+
148+
for file in &get_fixture_files().unwrap() {
149+
test_rustfix_with_file(file).unwrap();
150+
info!("passed: {:?}", file);
151+
}
152+
}

tests/fixtures.rs

-103
This file was deleted.

tests/fixtures/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.recorded.json

tests/fixtures/E0178.fixed.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
trait Foo {}
2+
3+
struct Bar<'a> {
4+
w: &'a (Foo + Copy),
5+
}
6+
7+
fn main() {
8+
}

tests/fixtures/E0178.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"message":"expected a path on the left-hand side of `+`, not `&'a Foo`","code":{"code":"E0178","explanation":"\nIn types, the `+` type operator has low precedence, so it is often necessary\nto use parentheses.\n\nFor example:\n\n```compile_fail,E0178\ntrait Foo {}\n\nstruct Bar<'a> {\n w: &'a Foo + Copy, // error, use &'a (Foo + Copy)\n x: &'a Foo + 'a, // error, use &'a (Foo + 'a)\n y: &'a mut Foo + 'a, // error, use &'a mut (Foo + 'a)\n z: fn() -> Foo + 'a, // error, use fn() -> (Foo + 'a)\n}\n```\n\nMore details can be found in [RFC 438].\n\n[RFC 438]: https://github.com/rust-lang/rfcs/pull/438\n"},"level":"error","spans":[{"file_name":"./tests/fixtures/E0178.rs","byte_start":38,"byte_end":52,"line_start":4,"line_end":4,"column_start":8,"column_end":22,"is_primary":true,"text":[{"text":" w: &'a Foo + Copy,","highlight_start":8,"highlight_end":22}],"label":null,"suggested_replacement":null,"expansion":null}],"children":[{"message":"try adding parentheses","code":null,"level":"help","spans":[{"file_name":"./tests/fixtures/E0178.rs","byte_start":38,"byte_end":52,"line_start":4,"line_end":4,"column_start":8,"column_end":22,"is_primary":true,"text":[{"text":" w: &'a Foo + Copy,","highlight_start":8,"highlight_end":22}],"label":null,"suggested_replacement":"&'a (Foo + Copy)","expansion":null}],"children":[],"rendered":null}],"rendered":"error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo`\n --> ./tests/fixtures/E0178.rs:4:8\n |\n4 | w: &'a Foo + Copy,\n | ^^^^^^^^^^^^^^ help: try adding parentheses: `&'a (Foo + Copy)`\n\n"}

tests/fixtures/E0178.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
trait Foo {}
2+
3+
struct Bar<'a> {
4+
w: &'a Foo + Copy,
5+
}
6+
7+
fn main() {
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2017 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+
// Point at the captured immutable outer variable
12+
13+
fn foo(mut f: Box<FnMut()>) {
14+
f();
15+
}
16+
17+
fn main() {
18+
let mut y = true;
19+
foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"message":"cannot assign to captured outer variable in an `FnMut` closure","code":{"code":"E0594","explanation":null},"level":"error","spans":[{"file_name":"./tests/fixtures/closure-immutable-outer-variable.rs","byte_start":615,"byte_end":624,"line_start":19,"line_end":19,"column_start":26,"column_end":35,"is_primary":true,"text":[{"text":" foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable","highlight_start":26,"highlight_end":35}],"label":null,"suggested_replacement":null,"expansion":null}],"children":[{"message":"consider making `y` mutable","code":null,"level":"help","spans":[{"file_name":"./tests/fixtures/closure-immutable-outer-variable.rs","byte_start":580,"byte_end":581,"line_start":18,"line_end":18,"column_start":9,"column_end":10,"is_primary":true,"text":[{"text":" let y = true;","highlight_start":9,"highlight_end":10}],"label":null,"suggested_replacement":"mut y","expansion":null}],"children":[],"rendered":null}],"rendered":"error[E0594]: cannot assign to captured outer variable in an `FnMut` closure\n --> ./tests/fixtures/closure-immutable-outer-variable.rs:19:26\n |\n18 | let y = true;\n | - help: consider making `y` mutable: `mut y`\n19 | foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable\n | ^^^^^^^^^\n\n"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2017 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+
// Point at the captured immutable outer variable
12+
13+
fn foo(mut f: Box<FnMut()>) {
14+
f();
15+
}
16+
17+
fn main() {
18+
let y = true;
19+
foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable
20+
}
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
let _x: &i32 = &&&&&5;
3+
}

tests/fixtures/needless_borrow.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"message":"this expression borrows a reference that is immediately dereferenced by the compiler","code":{"code":"needless_borrow","explanation":null},"level":"warning","spans":[{"file_name":"./tests/fixtures/needless_borrow.rs","byte_start":31,"byte_end":38,"line_start":2,"line_end":2,"column_start":20,"column_end":27,"is_primary":true,"text":[{"text":" let _x: &i32 = &&&&&&5;","highlight_start":20,"highlight_end":27}],"label":null,"suggested_replacement":null,"expansion":null}],"children":[{"message":"#[warn(needless_borrow)] on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"for further information visit https://rust-lang-nursery.github.io/rust-clippy/v0.0.177/index.html#needless_borrow","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"change this to","code":null,"level":"help","spans":[{"file_name":"./tests/fixtures/needless_borrow.rs","byte_start":31,"byte_end":38,"line_start":2,"line_end":2,"column_start":20,"column_end":27,"is_primary":true,"text":[{"text":" let _x: &i32 = &&&&&&5;","highlight_start":20,"highlight_end":27}],"label":null,"suggested_replacement":"&&&&&5","expansion":null}],"children":[],"rendered":null}],"rendered":"warning: this expression borrows a reference that is immediately dereferenced by the compiler\n --> ./tests/fixtures/needless_borrow.rs:2:20\n |\n2 | let _x: &i32 = &&&&&&5;\n | ^^^^^^^ help: change this to: `&&&&&5`\n |\n = note: #[warn(needless_borrow)] on by default\n = help: for further information visit https://rust-lang-nursery.github.io/rust-clippy/v0.0.177/index.html#needless_borrow\n\n"}

tests/fixtures/needless_borrow.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
let _x: &i32 = &&&&&&5;
3+
}

0 commit comments

Comments
 (0)