Skip to content

Commit d56ff94

Browse files
Regression tests (#422)
* Created regression tests for the table of contents * Refactoring to make the test more readable * Fixed some bitrot and removed the (now redundant) tests/helper module * Removed the include_str!() stuff and use just the dummy book for testing * Regression tests now pass again! * Pinned a `*` dependency to use a particular version * Made sure test mocks return errors instead of panicking * Addressed the rest of @budziq's review * Replaced a file open/read with file_to_string
1 parent fb99276 commit d56ff94

File tree

12 files changed

+255
-132
lines changed

12 files changed

+255
-132
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ ws = { version = "0.7", optional = true}
4343
[build-dependencies]
4444
error-chain = "0.11"
4545

46+
[dev-dependencies]
47+
select = "0.4"
48+
pretty_assertions = "0.4"
49+
walkdir = "1.0"
50+
4651
[features]
4752
default = ["output", "watch", "serve"]
4853
debug = []

tests/dummy/mod.rs

Lines changed: 0 additions & 87 deletions
This file was deleted.

tests/dummy_book/mod.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//! This will create an entire book in a temporary directory using some
2+
//! dummy contents from the `tests/dummy-book/` directory.
3+
4+
// Not all features are used in all test crates, so...
5+
#![allow(dead_code, unused_variables, unused_imports, unused_extern_crates)]
6+
extern crate mdbook;
7+
extern crate tempdir;
8+
extern crate walkdir;
9+
10+
use std::path::Path;
11+
use std::fs::{self, File};
12+
use std::io::{Read, Write};
13+
use mdbook::errors::*;
14+
use mdbook::utils::fs::file_to_string;
15+
16+
// The funny `self::` here is because we've got an `extern crate ...` and are
17+
// in a submodule
18+
use self::tempdir::TempDir;
19+
use self::mdbook::MDBook;
20+
use self::walkdir::WalkDir;
21+
22+
23+
/// Create a dummy book in a temporary directory, using the contents of
24+
/// `SUMMARY_MD` as a guide.
25+
///
26+
/// The "Nested Chapter" file contains a code block with a single
27+
/// `assert!($TEST_STATUS)`. If you want to check MDBook's testing
28+
/// functionality, `$TEST_STATUS` can be substitute for either `true` or
29+
/// `false`. This is done using the `passing_test` parameter.
30+
#[derive(Clone, Debug, PartialEq)]
31+
pub struct DummyBook {
32+
passing_test: bool,
33+
}
34+
35+
impl DummyBook {
36+
/// Create a new `DummyBook` with all the defaults.
37+
pub fn new() -> DummyBook {
38+
DummyBook { passing_test: true }
39+
}
40+
41+
/// Whether the doc-test included in the "Nested Chapter" should pass or
42+
/// fail (it passes by default).
43+
pub fn with_passing_test(&mut self, test_passes: bool) -> &mut DummyBook {
44+
self.passing_test = test_passes;
45+
self
46+
}
47+
48+
/// Write a book to a temporary directory using the provided settings.
49+
pub fn build(&self) -> Result<TempDir> {
50+
let temp = TempDir::new("dummy_book").chain_err(|| "Unable to create temp directory")?;
51+
52+
let dummy_book_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/dummy_book");
53+
recursive_copy(&dummy_book_root, temp.path()).chain_err(|| {
54+
"Couldn't copy files into a \
55+
temporary directory"
56+
})?;
57+
58+
let sub_pattern = if self.passing_test { "true" } else { "false" };
59+
let file_containing_test = temp.path().join("src/first/nested.md");
60+
replace_pattern_in_file(&file_containing_test, "$TEST_STATUS", sub_pattern)?;
61+
62+
Ok(temp)
63+
}
64+
}
65+
66+
fn replace_pattern_in_file(filename: &Path, from: &str, to: &str) -> Result<()> {
67+
let contents = file_to_string(filename)?;
68+
File::create(filename)?.write_all(contents.replace(from, to).as_bytes())?;
69+
70+
Ok(())
71+
}
72+
73+
/// Read the contents of the provided file into memory and then iterate through
74+
/// the list of strings asserting that the file contains all of them.
75+
pub fn assert_contains_strings<P: AsRef<Path>>(filename: P, strings: &[&str]) {
76+
let filename = filename.as_ref();
77+
let content = file_to_string(filename).expect("Couldn't read the file's contents");
78+
79+
for s in strings {
80+
assert!(content.contains(s),
81+
"Searching for {:?} in {}\n\n{}",
82+
s,
83+
filename.display(),
84+
content);
85+
}
86+
}
87+
88+
89+
90+
/// Recursively copy an entire directory tree to somewhere else (a la `cp -r`).
91+
fn recursive_copy<A: AsRef<Path>, B: AsRef<Path>>(from: A, to: B) -> Result<()> {
92+
let from = from.as_ref();
93+
let to = to.as_ref();
94+
95+
for entry in WalkDir::new(&from) {
96+
let entry = entry.chain_err(|| "Unable to inspect directory entry")?;
97+
98+
let original_location = entry.path();
99+
let relative = original_location.strip_prefix(&from)
100+
.expect("`original_location` is inside the `from` \
101+
directory");
102+
let new_location = to.join(relative);
103+
104+
if original_location.is_file() {
105+
if let Some(parent) = new_location.parent() {
106+
fs::create_dir_all(parent).chain_err(|| "Couldn't create directory")?;
107+
}
108+
109+
fs::copy(&original_location, &new_location).chain_err(|| {
110+
"Unable to copy file contents"
111+
})?;
112+
}
113+
}
114+
115+
Ok(())
116+
}

tests/dummy/book/SUMMARY.md renamed to tests/dummy_book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- [First Chapter](./first/index.md)
66
- [Nested Chapter](./first/nested.md)
7+
---
78
- [Second Chapter](./second.md)
89

910
[Conclusion](./conclusion.md)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

tests/helpers/mod.rs

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)