diff --git a/Cargo.toml b/Cargo.toml
index 964d006..e6d1321 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,9 +27,9 @@ log = "0.4.1"
 duct = "0.8.2"
 env_logger = "0.5.0-rc.1"
 log = "0.4.1"
-pretty_assertions = "0.4.1"
 tempdir = "0.3.5"
 proptest = "0.7.0"
+difference = "2.0.0"
 
 [workspace]
 members = [
diff --git a/tests/fixtures/.gitignore b/tests/edition/.gitignore
similarity index 100%
rename from tests/fixtures/.gitignore
rename to tests/edition/.gitignore
diff --git a/tests/everything.rs b/tests/everything.rs
deleted file mode 100644
index 0d42e1a..0000000
--- a/tests/everything.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-#![cfg(not(windows))] // TODO: should fix these tests on Windows
-
-#[macro_use]
-extern crate duct;
-extern crate env_logger;
-#[macro_use]
-extern crate log;
-#[macro_use]
-extern crate pretty_assertions;
-extern crate rustfix;
-extern crate serde_json;
-extern crate tempdir;
-
-use std::fs;
-use std::error::Error;
-use std::path::{Path, PathBuf};
-use std::collections::HashSet;
-use std::process::Output;
-use tempdir::TempDir;
-
-use rustfix::apply_suggestions;
-
-fn compile(file: &Path) -> Result<Output, Box<Error>> {
-    let tmp = TempDir::new("rustfix-tests")?;
-    let better_call_clippy = cmd!(
-        "rustc",
-        file,
-        "--error-format=pretty-json",
-        "-Zunstable-options",
-        "--emit=metadata",
-        "--crate-name=rustfix_test",
-        "-Zsuggestion-applicability",
-        "--out-dir",
-        tmp.path()
-    );
-    let res = better_call_clippy
-        .env("CLIPPY_DISABLE_DOCS_LINKS", "true")
-        .stdout_capture()
-        .stderr_capture()
-        .unchecked()
-        .run()?;
-
-    Ok(res)
-}
-
-fn compile_and_get_json_errors(file: &Path) -> Result<String, Box<Error>> {
-    let res = compile(file)?;
-    let stderr = String::from_utf8(res.stderr)?;
-
-    use std::io::{Error, ErrorKind};
-    match res.status.code() {
-        Some(0) | Some(1) | Some(101) => Ok(stderr),
-        _ => Err(Box::new(Error::new(
-            ErrorKind::Other,
-            format!("failed with status {:?}: {}", res.status.code(), stderr),
-        ))),
-    }
-}
-
-fn compiles_without_errors(file: &Path) -> Result<(), Box<Error>> {
-    let res = compile(file)?;
-
-    use std::io::{Error, ErrorKind};
-    match res.status.code() {
-        Some(0) => Ok(()),
-        _ => {
-            info!(
-                "file {:?} failed to compile:\n{}",
-                file,
-                String::from_utf8(res.stderr)?
-            );
-            Err(Box::new(Error::new(
-                ErrorKind::Other,
-                format!(
-                    "failed with status {:?} (`env RUST_LOG=everything=info` for more info)",
-                    res.status.code(),
-                ),
-            )))
-        }
-    }
-}
-
-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 test_rustfix_with_file<P: AsRef<Path>>(file: P) -> Result<(), Box<Error>> {
-    let file: &Path = file.as_ref();
-    let json_file = file.with_extension("json");
-    let fixed_file = file.with_extension("fixed.rs");
-
-    debug!("next up: {:?}", file);
-    let code = read_file(file)?;
-    let errors = compile_and_get_json_errors(file)?;
-    let suggestions = rustfix::get_suggestions_from_json(&errors, &HashSet::new())
-        .expect("could not load suggestions");
-
-    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(&json_file)?;
-    let expected_suggestions = rustfix::get_suggestions_from_json(&expected_json, &HashSet::new())
-        .expect("could not load expected suggesitons");
-    assert_eq!(
-        expected_suggestions, suggestions,
-        "got unexpected suggestions from clippy",
-    );
-
-    let fixed = apply_suggestions(&code, &suggestions)?;
-
-    if std::env::var("RUSTFIX_TEST_RECORD_FIXED_RUST").is_ok() {
-        use std::io::Write;
-        let mut recorded_rust = fs::File::create(&file.with_extension("recorded.rs"))?;
-        recorded_rust.write_all(fixed.as_bytes())?;
-    }
-
-    let expected_fixed = read_file(&fixed_file)?;
-    assert_eq!(
-        fixed.trim(),
-        expected_fixed.trim(),
-        "file {} doesn't look fixed",
-        file.display()
-    );
-
-    compiles_without_errors(&fixed_file)?;
-
-    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") && !x.ends_with(".recorded.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);
-    }
-}
diff --git a/tests/everything/.gitignore b/tests/everything/.gitignore
new file mode 100644
index 0000000..bfb599d
--- /dev/null
+++ b/tests/everything/.gitignore
@@ -0,0 +1,2 @@
+*.recorded.json
+*.recorded.rs
diff --git a/tests/fixtures/E0178.fixed.rs b/tests/everything/E0178.fixed.rs
similarity index 100%
rename from tests/fixtures/E0178.fixed.rs
rename to tests/everything/E0178.fixed.rs
diff --git a/tests/fixtures/E0178.json b/tests/everything/E0178.json
similarity index 84%
rename from tests/fixtures/E0178.json
rename to tests/everything/E0178.json
index 52ac88c..89f15b5 100644
--- a/tests/fixtures/E0178.json
+++ b/tests/everything/E0178.json
@@ -7,7 +7,7 @@
   "level": "error",
   "spans": [
     {
-      "file_name": "./tests/fixtures/E0178.rs",
+      "file_name": "./tests/everything/E0178.rs",
       "byte_start": 60,
       "byte_end": 74,
       "line_start": 6,
@@ -34,7 +34,7 @@
       "level": "help",
       "spans": [
         {
-          "file_name": "./tests/fixtures/E0178.rs",
+          "file_name": "./tests/everything/E0178.rs",
           "byte_start": 60,
           "byte_end": 74,
           "line_start": 6,
@@ -58,7 +58,7 @@
       "rendered": null
     }
   ],
-  "rendered": "error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo`\n --> ./tests/fixtures/E0178.rs:6:8\n  |\n6 |     w: &'a Foo + Send,\n  |        ^^^^^^^^^^^^^^ help: try adding parentheses: `&'a (Foo + Send)`\n\nIf you want more information on this error, try using \"rustc --explain E0178\"\n"
+  "rendered": "error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo`\n --> ./tests/everything/E0178.rs:6:8\n  |\n6 |     w: &'a Foo + Send,\n  |        ^^^^^^^^^^^^^^ help: try adding parentheses: `&'a (Foo + Send)`\n\nIf you want more information on this error, try using \"rustc --explain E0178\"\n"
 }
 {
   "message": "aborting due to previous error",
diff --git a/tests/fixtures/E0178.rs b/tests/everything/E0178.rs
similarity index 100%
rename from tests/fixtures/E0178.rs
rename to tests/everything/E0178.rs
diff --git a/tests/fixtures/closure-immutable-outer-variable.fixed.rs b/tests/everything/closure-immutable-outer-variable.fixed.rs
similarity index 100%
rename from tests/fixtures/closure-immutable-outer-variable.fixed.rs
rename to tests/everything/closure-immutable-outer-variable.fixed.rs
diff --git a/tests/fixtures/closure-immutable-outer-variable.json b/tests/everything/closure-immutable-outer-variable.json
similarity index 73%
rename from tests/fixtures/closure-immutable-outer-variable.json
rename to tests/everything/closure-immutable-outer-variable.json
index 3ecfb41..f7afa49 100644
--- a/tests/fixtures/closure-immutable-outer-variable.json
+++ b/tests/everything/closure-immutable-outer-variable.json
@@ -7,7 +7,7 @@
   "level": "error",
   "spans": [
     {
-      "file_name": "./tests/fixtures/closure-immutable-outer-variable.rs",
+      "file_name": "./tests/everything/closure-immutable-outer-variable.rs",
       "byte_start": 615,
       "byte_end": 624,
       "line_start": 19,
@@ -34,7 +34,7 @@
       "level": "help",
       "spans": [
         {
-          "file_name": "./tests/fixtures/closure-immutable-outer-variable.rs",
+          "file_name": "./tests/everything/closure-immutable-outer-variable.rs",
           "byte_start": 580,
           "byte_end": 581,
           "line_start": 18,
@@ -58,7 +58,7 @@
       "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\nIf you want more information on this error, try using \"rustc --explain E0594\"\n"
+  "rendered": "error[E0594]: cannot assign to captured outer variable in an `FnMut` closure\n  --> ./tests/everything/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\nIf you want more information on this error, try using \"rustc --explain E0594\"\n"
 }
 {
   "message": "aborting due to previous error",
diff --git a/tests/fixtures/closure-immutable-outer-variable.rs b/tests/everything/closure-immutable-outer-variable.rs
similarity index 100%
rename from tests/fixtures/closure-immutable-outer-variable.rs
rename to tests/everything/closure-immutable-outer-variable.rs
diff --git a/tests/fixtures/lt-generic-comp.fixed.rs b/tests/everything/lt-generic-comp.fixed.rs
similarity index 100%
rename from tests/fixtures/lt-generic-comp.fixed.rs
rename to tests/everything/lt-generic-comp.fixed.rs
diff --git a/tests/fixtures/lt-generic-comp.json b/tests/everything/lt-generic-comp.json
similarity index 79%
rename from tests/fixtures/lt-generic-comp.json
rename to tests/everything/lt-generic-comp.json
index 4230585..0634762 100644
--- a/tests/fixtures/lt-generic-comp.json
+++ b/tests/everything/lt-generic-comp.json
@@ -4,7 +4,7 @@
   "level": "error",
   "spans": [
     {
-      "file_name": "./tests/fixtures/lt-generic-comp.rs",
+      "file_name": "./tests/everything/lt-generic-comp.rs",
       "byte_start": 49,
       "byte_end": 50,
       "line_start": 4,
@@ -24,7 +24,7 @@
       "expansion": null
     },
     {
-      "file_name": "./tests/fixtures/lt-generic-comp.rs",
+      "file_name": "./tests/everything/lt-generic-comp.rs",
       "byte_start": 47,
       "byte_end": 48,
       "line_start": 4,
@@ -51,7 +51,7 @@
       "level": "help",
       "spans": [
         {
-          "file_name": "./tests/fixtures/lt-generic-comp.rs",
+          "file_name": "./tests/everything/lt-generic-comp.rs",
           "byte_start": 38,
           "byte_end": 46,
           "line_start": 4,
@@ -75,7 +75,7 @@
       "rendered": null
     }
   ],
-  "rendered": "error: `<` is interpreted as a start of generic arguments for `u32`, not a comparison\n --> ./tests/fixtures/lt-generic-comp.rs:4:17\n  |\n4 |     if x as u32 < 4 {\n  |        -------- ^ - interpreted as generic arguments\n  |        |        |\n  |        |        not interpreted as comparison\n  |        help: try comparing the cast value: `(x as u32)`\n\n"
+  "rendered": "error: `<` is interpreted as a start of generic arguments for `u32`, not a comparison\n --> ./tests/everything/lt-generic-comp.rs:4:17\n  |\n4 |     if x as u32 < 4 {\n  |        -------- ^ - interpreted as generic arguments\n  |        |        |\n  |        |        not interpreted as comparison\n  |        help: try comparing the cast value: `(x as u32)`\n\n"
 }
 {
   "message": "aborting due to previous error",
diff --git a/tests/fixtures/lt-generic-comp.rs b/tests/everything/lt-generic-comp.rs
similarity index 100%
rename from tests/fixtures/lt-generic-comp.rs
rename to tests/everything/lt-generic-comp.rs
diff --git a/tests/fixtures/str-lit-type-mismatch.fixed.rs b/tests/everything/str-lit-type-mismatch.fixed.rs
similarity index 100%
rename from tests/fixtures/str-lit-type-mismatch.fixed.rs
rename to tests/everything/str-lit-type-mismatch.fixed.rs
diff --git a/tests/fixtures/str-lit-type-mismatch.json b/tests/everything/str-lit-type-mismatch.json
similarity index 76%
rename from tests/fixtures/str-lit-type-mismatch.json
rename to tests/everything/str-lit-type-mismatch.json
index 0e3e764..1c85251 100644
--- a/tests/fixtures/str-lit-type-mismatch.json
+++ b/tests/everything/str-lit-type-mismatch.json
@@ -7,7 +7,7 @@
   "level": "error",
   "spans": [
     {
-      "file_name": "./tests/fixtures/str-lit-type-mismatch.rs",
+      "file_name": "./tests/everything/str-lit-type-mismatch.rs",
       "byte_start": 499,
       "byte_end": 504,
       "line_start": 13,
@@ -42,7 +42,7 @@
       "level": "help",
       "spans": [
         {
-          "file_name": "./tests/fixtures/str-lit-type-mismatch.rs",
+          "file_name": "./tests/everything/str-lit-type-mismatch.rs",
           "byte_start": 499,
           "byte_end": 504,
           "line_start": 13,
@@ -66,7 +66,7 @@
       "rendered": null
     }
   ],
-  "rendered": "error[E0308]: mismatched types\n  --> ./tests/fixtures/str-lit-type-mismatch.rs:13:20\n   |\n13 |     let x: &[u8] = \"foo\"; //~ ERROR mismatched types\n   |                    ^^^^^\n   |                    |\n   |                    expected slice, found str\n   |                    help: consider adding a leading `b`: `b\"foo\"`\n   |\n   = note: expected type `&[u8]`\n              found type `&'static str`\n\nIf you want more information on this error, try using \"rustc --explain E0308\"\n"
+  "rendered": "error[E0308]: mismatched types\n  --> ./tests/everything/str-lit-type-mismatch.rs:13:20\n   |\n13 |     let x: &[u8] = \"foo\"; //~ ERROR mismatched types\n   |                    ^^^^^\n   |                    |\n   |                    expected slice, found str\n   |                    help: consider adding a leading `b`: `b\"foo\"`\n   |\n   = note: expected type `&[u8]`\n              found type `&'static str`\n\nIf you want more information on this error, try using \"rustc --explain E0308\"\n"
 }
 {
   "message": "mismatched types",
@@ -77,7 +77,7 @@
   "level": "error",
   "spans": [
     {
-      "file_name": "./tests/fixtures/str-lit-type-mismatch.rs",
+      "file_name": "./tests/everything/str-lit-type-mismatch.rs",
       "byte_start": 555,
       "byte_end": 561,
       "line_start": 14,
@@ -112,7 +112,7 @@
       "level": "help",
       "spans": [
         {
-          "file_name": "./tests/fixtures/str-lit-type-mismatch.rs",
+          "file_name": "./tests/everything/str-lit-type-mismatch.rs",
           "byte_start": 555,
           "byte_end": 561,
           "line_start": 14,
@@ -136,7 +136,7 @@
       "rendered": null
     }
   ],
-  "rendered": "error[E0308]: mismatched types\n  --> ./tests/fixtures/str-lit-type-mismatch.rs:14:23\n   |\n14 |     let y: &[u8; 4] = \"baaa\"; //~ ERROR mismatched types\n   |                       ^^^^^^\n   |                       |\n   |                       expected array of 4 elements, found str\n   |                       help: consider adding a leading `b`: `b\"baaa\"`\n   |\n   = note: expected type `&[u8; 4]`\n              found type `&'static str`\n\nIf you want more information on this error, try using \"rustc --explain E0308\"\n"
+  "rendered": "error[E0308]: mismatched types\n  --> ./tests/everything/str-lit-type-mismatch.rs:14:23\n   |\n14 |     let y: &[u8; 4] = \"baaa\"; //~ ERROR mismatched types\n   |                       ^^^^^^\n   |                       |\n   |                       expected array of 4 elements, found str\n   |                       help: consider adding a leading `b`: `b\"baaa\"`\n   |\n   = note: expected type `&[u8; 4]`\n              found type `&'static str`\n\nIf you want more information on this error, try using \"rustc --explain E0308\"\n"
 }
 {
   "message": "mismatched types",
@@ -147,7 +147,7 @@
   "level": "error",
   "spans": [
     {
-      "file_name": "./tests/fixtures/str-lit-type-mismatch.rs",
+      "file_name": "./tests/everything/str-lit-type-mismatch.rs",
       "byte_start": 608,
       "byte_end": 614,
       "line_start": 15,
@@ -182,7 +182,7 @@
       "level": "help",
       "spans": [
         {
-          "file_name": "./tests/fixtures/str-lit-type-mismatch.rs",
+          "file_name": "./tests/everything/str-lit-type-mismatch.rs",
           "byte_start": 608,
           "byte_end": 614,
           "line_start": 15,
@@ -206,7 +206,7 @@
       "rendered": null
     }
   ],
-  "rendered": "error[E0308]: mismatched types\n  --> ./tests/fixtures/str-lit-type-mismatch.rs:15:19\n   |\n15 |     let z: &str = b\"foo\"; //~ ERROR mismatched types\n   |                   ^^^^^^\n   |                   |\n   |                   expected str, found array of 3 elements\n   |                   help: consider removing the leading `b`: `\"foo\"`\n   |\n   = note: expected type `&str`\n              found type `&'static [u8; 3]`\n\nIf you want more information on this error, try using \"rustc --explain E0308\"\n"
+  "rendered": "error[E0308]: mismatched types\n  --> ./tests/everything/str-lit-type-mismatch.rs:15:19\n   |\n15 |     let z: &str = b\"foo\"; //~ ERROR mismatched types\n   |                   ^^^^^^\n   |                   |\n   |                   expected str, found array of 3 elements\n   |                   help: consider removing the leading `b`: `\"foo\"`\n   |\n   = note: expected type `&str`\n              found type `&'static [u8; 3]`\n\nIf you want more information on this error, try using \"rustc --explain E0308\"\n"
 }
 {
   "message": "aborting due to 3 previous errors",
diff --git a/tests/fixtures/str-lit-type-mismatch.rs b/tests/everything/str-lit-type-mismatch.rs
similarity index 100%
rename from tests/fixtures/str-lit-type-mismatch.rs
rename to tests/everything/str-lit-type-mismatch.rs
diff --git a/tests/parse_and_replace.rs b/tests/parse_and_replace.rs
new file mode 100644
index 0000000..9d10e94
--- /dev/null
+++ b/tests/parse_and_replace.rs
@@ -0,0 +1,239 @@
+#![cfg(not(windows))] // TODO: should fix these tests on Windows
+
+extern crate duct;
+extern crate env_logger;
+#[macro_use]
+extern crate log;
+extern crate rustfix;
+extern crate serde_json;
+extern crate tempdir;
+#[macro_use]
+extern crate failure;
+extern crate difference;
+
+use std::ffi::OsString;
+use std::{env, fs};
+use std::path::{Path, PathBuf};
+use std::collections::HashSet;
+use std::process::Output;
+
+use failure::{Error, ResultExt};
+use tempdir::TempDir;
+
+use rustfix::apply_suggestions;
+
+mod fixmode {
+    pub const EVERYTHING: &str = "yolo";
+    pub const EDITION: &str = "edition";
+}
+
+mod settings {
+    // can be set as env var to debug
+    pub const CHECK_JSON: &str = "RUSTFIX_TEST_CHECK_JSON";
+    pub const RECORD_JSON: &str = "RUSTFIX_TEST_RECORD_JSON";
+    pub const RECORD_FIXED_RUST: &str = "RUSTFIX_TEST_RECORD_FIXED_RUST";
+
+    // set automatically
+    pub const MODE: &str = "RUSTFIX_MODE";
+}
+
+fn compile(file: &Path, mode: &str) -> Result<Output, Error> {
+    let tmp = TempDir::new("rustfix-tests")?;
+
+    let mut args: Vec<OsString> = vec![
+        file.into(),
+        "--error-format=pretty-json".into(),
+        "-Zunstable-options".into(),
+        "--emit=metadata".into(),
+        "--crate-name=rustfix_test".into(),
+        "-Zsuggestion-applicability".into(),
+        "--out-dir".into(),
+        tmp.path().into(),
+    ];
+
+    if mode == fixmode::EDITION {
+        args.push("--edition=2018".into());
+    }
+
+    let res = duct::cmd("rustc", &args)
+        .env("CLIPPY_DISABLE_DOCS_LINKS", "true")
+        .stdout_capture()
+        .stderr_capture()
+        .unchecked()
+        .run()?;
+
+    Ok(res)
+}
+
+fn compile_and_get_json_errors(file: &Path, mode: &str) -> Result<String, Error> {
+    let res = compile(file, mode)?;
+    let stderr = String::from_utf8(res.stderr)?;
+
+    match res.status.code() {
+        Some(0) | Some(1) | Some(101) => Ok(stderr),
+        _ => Err(format_err!("failed with status {:?}: {}", res.status.code(), stderr)),
+    }
+}
+
+fn compiles_without_errors(file: &Path, mode: &str) -> Result<(), Error> {
+    let res = compile(file, mode)?;
+
+    match res.status.code() {
+        Some(0) => Ok(()),
+        _ => {
+            info!(
+                "file {:?} failed to compile:\n{}",
+                file,
+                String::from_utf8(res.stderr)?
+            );
+            Err(format_err!(
+                "failed with status {:?} (`env RUST_LOG=parse_and_replace=info` for more info)",
+                res.status.code(),
+            ))
+        }
+    }
+}
+
+fn read_file(path: &Path) -> Result<String, 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 diff(expected: &str, actual: &str) -> String {
+    use std::fmt::Write;
+    use difference::{Changeset, Difference};
+
+    let mut res = String::new();
+    let changeset = Changeset::new(expected.trim(), actual.trim(), "\n");
+
+    let mut different = false;
+    for diff in changeset.diffs {
+        let (prefix, diff) = match diff {
+            Difference::Same(_) => continue,
+            Difference::Add(add) => ("+", add),
+            Difference::Rem(rem) => ("-", rem),
+        };
+        if !different {
+            write!(&mut res, "differences found (+ == actual, - == expected):\n");
+            different = true;
+        }
+        for diff in diff.lines() {
+            writeln!(&mut res, "{} {}", prefix, diff);
+        }
+    }
+    if different {
+        write!(&mut res, "");
+    }
+
+    res
+}
+
+fn test_rustfix_with_file<P: AsRef<Path>>(file: P, mode: &str) -> Result<(), Error> {
+    let file: &Path = file.as_ref();
+    let json_file = file.with_extension("json");
+    let fixed_file = file.with_extension("fixed.rs");
+
+    debug!("next up: {:?}", file);
+    let code = read_file(file)
+        .context(format!("could not read {}", file.display()))?;
+    let errors = compile_and_get_json_errors(file, mode)
+        .context(format!("could compile {}", file.display()))?;
+    let suggestions = rustfix::get_suggestions_from_json(&errors, &HashSet::new())
+        .context("could not load suggestions")?;
+
+    if std::env::var(settings::RECORD_JSON).is_ok() {
+        use std::io::Write;
+        let mut recorded_json = fs::File::create(&file.with_extension("recorded.json"))
+            .context(format!("could not create recorded.json for {}", file.display()))?;
+        recorded_json.write_all(errors.as_bytes())?;
+    }
+
+    if std::env::var(settings::CHECK_JSON).is_ok() {
+        let expected_json = read_file(&json_file)
+            .context(format!("could not load json fixtures for {}", file.display()))?;;
+        let expected_suggestions = rustfix::get_suggestions_from_json(&expected_json, &HashSet::new())
+            .context("could not load expected suggesitons")?;
+
+        ensure!(
+            expected_suggestions == suggestions,
+            "got unexpected suggestions from clippy:\n{}",
+            diff(&format!("{:?}", expected_suggestions), &format!("{:?}", suggestions))
+        );
+    }
+
+    let fixed = apply_suggestions(&code, &suggestions)
+        .context(format!("could not apply suggestions to {}", file.display()))?;
+
+    if std::env::var(settings::RECORD_FIXED_RUST).is_ok() {
+        use std::io::Write;
+        let mut recorded_rust = fs::File::create(&file.with_extension("recorded.rs"))?;
+        recorded_rust.write_all(fixed.as_bytes())?;
+    }
+
+    let expected_fixed = read_file(&fixed_file)
+        .context(format!("could read fixed file for {}", file.display()))?;
+    ensure!(
+        fixed.trim() == expected_fixed.trim(),
+        "file {} doesn't look fixed:\n{}", file.display(), diff(fixed.trim(), expected_fixed.trim())
+    );
+
+    compiles_without_errors(&fixed_file, mode)?;
+
+    Ok(())
+}
+
+fn get_fixture_files(p: &str) -> Result<Vec<PathBuf>, Error> {
+    Ok(fs::read_dir(&p)?
+        .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") && !x.ends_with(".recorded.rs")
+        })
+        .collect())
+}
+
+fn assert_fixtures(dir: &str, mode: &str) {
+    let files = get_fixture_files(&dir)
+        .context(format!("couldn't load dir `{}`", dir))
+        .unwrap();
+    let mut failures = 0;
+
+    for file in &files {
+        if let Err(err) = test_rustfix_with_file(file, mode) {
+            println!("failed: {}", file.display());
+            warn!("{}", err);
+            for cause in err.causes().skip(1) {
+                info!("\tcaused by: {}", cause);
+            }
+            failures += 1;
+        }
+        info!("passed: {:?}", file);
+    }
+
+    if failures > 0 {
+        panic!(
+            "{} out of {} fixture asserts failed\n\
+            (run with `env RUST_LOG=parse_and_replace=info` to get more details)",
+            failures, files.len(),
+        );
+    }
+}
+
+#[test]
+fn everything() {
+    let _ = env_logger::try_init();
+    assert_fixtures("./tests/everything", fixmode::EVERYTHING);
+}
+
+#[test]
+#[ignore = "Requires custom rustc build"]
+fn edition() {
+    let _ = env_logger::try_init();
+    assert_fixtures("./tests/edition", fixmode::EDITION);
+}