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

Commit

Permalink
Merge pull request #44 from killercup/refactor/tests
Browse files Browse the repository at this point in the history
Refactor tests to use explicit fixtures
  • Loading branch information
killercup authored Jan 16, 2018
2 parents 2ef7bcc + 3c61332 commit 17507cf
Show file tree
Hide file tree
Showing 44 changed files with 273 additions and 900 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
[submodule "tests/fixtures/libui-rs"]
path = tests/crates/libui-rs
url = https://github.com/pcwalton/libui-rs.git
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ language: rust
rust:
- nightly
- stable

before_script:
- if [ $TRAVIS_RUST_VERSION == nightly ]; then cargo install clippy --force ; fi
script:
- cargo build
- if [ $TRAVIS_RUST_VERSION == nightly ]; then cargo test -- --nocapture ; fi
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ serde_derive = "1.0"

[dev-dependencies]
duct = "0.8.2"
env_logger = "0.5.0-rc.1"
log = "0.4.1"
pretty_assertions = "0.4.1"
tempdir = "0.3.5"
14 changes: 14 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ use std::collections::HashSet;
pub mod diagnostics;
use diagnostics::{Diagnostic, DiagnosticSpan};

pub fn get_suggestions_from_json(input: &str, only: &HashSet<String>) -> Vec<Suggestion> {
input.lines()
.filter(not_empty)
// Convert JSON string (and eat parsing errors)
.flat_map(|line| serde_json::from_str::<Diagnostic>(line))
// One diagnostic line might have multiple suggestions
.filter_map(|cargo_msg| collect_suggestions(&cargo_msg, only))
.collect()
}

#[derive(Debug, Copy, Clone, Hash, PartialEq)]
pub struct LinePosition {
pub line: usize,
Expand Down Expand Up @@ -153,3 +163,7 @@ pub fn collect_suggestions(diagnostic: &Diagnostic, only: &HashSet<String>) -> O
})
}
}

fn not_empty(s: &&str) -> bool {
!s.trim().is_empty()
}
1 change: 0 additions & 1 deletion tests/crates/libui-rs
Submodule libui-rs deleted from 13299d
1 change: 0 additions & 1 deletion tests/crates/libui-rs.sub-path

This file was deleted.

6 changes: 0 additions & 6 deletions tests/crates/use_test/Cargo.toml

This file was deleted.

3 changes: 0 additions & 3 deletions tests/crates/use_test/src/lib.rs

This file was deleted.

152 changes: 152 additions & 0 deletions tests/everything.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#[macro_use] extern crate duct;
#[macro_use] extern crate pretty_assertions;
extern crate tempdir;
#[macro_use] extern crate log;
extern crate env_logger;
extern crate serde_json;
extern crate rustfix;

use std::fs;
use std::error::Error;
use std::path::{Path, PathBuf};
use std::collections::HashSet;
use tempdir::TempDir;

use rustfix::Replacement;

fn compile_and_get_json_errors(file: &Path) -> Result<String, Box<Error>> {
let tmp = TempDir::new("rustfix-tests")?;
let better_call_clippy = cmd!(
"clippy-driver", "rustc", file,
"--error-format=json", "--emit=metadata",
"--out-dir", tmp.path()
);
let res = better_call_clippy
.stdout_capture()
.stderr_capture()
.unchecked()
.run()?;
let stderr = String::from_utf8(res.stderr)?;

use std::io::{Error, ErrorKind};
match res.status.code() {
Some(0) | Some(1) => Ok(stderr),
_ => Err(Box::new(Error::new(
ErrorKind::Other,
format!("failed with status {:?}: {}", res.status.code(), stderr),
)))
}
}

fn read_file(path: &Path) -> Result<String, Box<Error>> {
use std::io::Read;

let mut buffer = String::new();
let mut file = fs::File::open(path)?;
file.read_to_string(&mut buffer)?;
Ok(buffer)
}

fn apply_suggestion(file_content: &mut String, suggestion: &Replacement) -> Result<String, Box<Error>> {
use std::cmp::max;

let mut new_content = String::new();

// Add the lines before the section we want to replace
new_content.push_str(&file_content.lines()
.take(max(suggestion.snippet.line_range.start.line - 1, 0) as usize)
.collect::<Vec<_>>()
.join("\n"));
new_content.push_str("\n");

// Parts of line before replacement
new_content.push_str(&file_content.lines()
.nth(suggestion.snippet.line_range.start.line - 1)
.unwrap_or("")
.chars()
.take(suggestion.snippet.line_range.start.column - 1)
.collect::<String>());

// Insert new content! Finally!
new_content.push_str(&suggestion.replacement);

// Parts of line after replacement
new_content.push_str(&file_content.lines()
.nth(suggestion.snippet.line_range.end.line - 1)
.unwrap_or("")
.chars()
.skip(suggestion.snippet.line_range.end.column - 1)
.collect::<String>());

// Add the lines after the section we want to replace
new_content.push_str("\n");
new_content.push_str(&file_content.lines()
.skip(suggestion.snippet.line_range.end.line as usize)
.collect::<Vec<_>>()
.join("\n"));
new_content.push_str("\n");

Ok(new_content)
}

fn test_rustfix_with_file<P: AsRef<Path>>(file: P) -> Result<(), Box<Error>> {
let file: &Path = file.as_ref();
debug!("{:?}", file);
let code = read_file(&file)?;
let errors = compile_and_get_json_errors(file)?;

if std::env::var("RUSTFIX_TEST_RECORD_JSON").is_ok() {
use std::io::Write;
let mut recorded_json = fs::File::create(&file.with_extension("recorded.json"))?;
recorded_json.write_all(errors.as_bytes())?;
}

let expected_json = read_file(&file.with_extension("json"))?;

assert_eq!(
errors.trim(),
expected_json.trim(),
"got unexpected json from clippy"
);

let suggestions = rustfix::get_suggestions_from_json(&errors, &HashSet::new());
let mut fixed = code.clone();

for sug in suggestions {
trace!("{:?}", sug);
for sol in sug.solutions {
trace!("{:?}", sol);
for r in sol.replacements {
info!("replaced.");
trace!("{:?}", r);
fixed = apply_suggestion(&mut fixed, &r)?;
}
}
}

let expected_fixed = read_file(&file.with_extension("fixed.rs"))?;
assert_eq!(fixed.trim(), expected_fixed.trim(), "file doesn't look fixed");
Ok(())
}

fn get_fixture_files() -> Result<Vec<PathBuf>, Box<Error>> {
Ok(fs::read_dir("./tests/fixtures")?
.into_iter()
.map(|e| e.unwrap().path())
.filter(|p| p.is_file())
.filter(|p| {
let x = p.to_string_lossy();
x.ends_with(".rs") && !x.ends_with(".fixed.rs")
})
.collect())
}

#[test]
fn fixtures() {
let _ = env_logger::try_init();

for file in &get_fixture_files().unwrap() {
test_rustfix_with_file(file).unwrap();
info!("passed: {:?}", file);
}
}
103 changes: 0 additions & 103 deletions tests/fixtures.rs

This file was deleted.

1 change: 1 addition & 0 deletions tests/fixtures/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.recorded.json
8 changes: 8 additions & 0 deletions tests/fixtures/E0178.fixed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
trait Foo {}

struct Bar<'a> {
w: &'a (Foo + Copy),
}

fn main() {
}
1 change: 1 addition & 0 deletions tests/fixtures/E0178.json
Original file line number Diff line number Diff line change
@@ -0,0 +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"}
8 changes: 8 additions & 0 deletions tests/fixtures/E0178.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
trait Foo {}

struct Bar<'a> {
w: &'a Foo + Copy,
}

fn main() {
}
20 changes: 20 additions & 0 deletions tests/fixtures/closure-immutable-outer-variable.fixed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2017 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.

// Point at the captured immutable outer variable

fn foo(mut f: Box<FnMut()>) {
f();
}

fn main() {
let mut y = true;
foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable
}
1 change: 1 addition & 0 deletions tests/fixtures/closure-immutable-outer-variable.json
Original file line number Diff line number Diff line change
@@ -0,0 +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"}
20 changes: 20 additions & 0 deletions tests/fixtures/closure-immutable-outer-variable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2017 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.

// Point at the captured immutable outer variable

fn foo(mut f: Box<FnMut()>) {
f();
}

fn main() {
let y = true;
foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable
}
3 changes: 3 additions & 0 deletions tests/fixtures/needless_borrow.fixed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
let _x: &i32 = &&&&&5;
}
1 change: 1 addition & 0 deletions tests/fixtures/needless_borrow.json
Original file line number Diff line number Diff line change
@@ -0,0 +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"}
3 changes: 3 additions & 0 deletions tests/fixtures/needless_borrow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
let _x: &i32 = &&&&&&5;
}
Loading

0 comments on commit 17507cf

Please sign in to comment.