//! This will create an entire book in a temporary directory using some //! dummy contents from the `tests/dummy-book/` directory. // Not all features are used in all test crates, so... #![allow(dead_code, unused_variables, unused_imports, unused_extern_crates)] use anyhow::Context; use mdbook::errors::*; use mdbook::MDBook; use std::fs::{self, File}; use std::io::{Read, Write}; use std::path::Path; use tempfile::{Builder as TempFileBuilder, TempDir}; use walkdir::WalkDir; /// Create a dummy book in a temporary directory, using the contents of /// `SUMMARY_MD` as a guide. /// /// The "Nested Chapter" file contains a code block with a single /// `assert!($TEST_STATUS)`. If you want to check MDBook's testing /// functionality, `$TEST_STATUS` can be substitute for either `true` or /// `false`. This is done using the `passing_test` parameter. #[derive(Clone, Debug, PartialEq)] pub struct DummyBook { passing_test: bool, } impl DummyBook { /// Create a new `DummyBook` with all the defaults. pub fn new() -> DummyBook { DummyBook { passing_test: true } } /// Whether the doc-test included in the "Nested Chapter" should pass or /// fail (it passes by default). pub fn with_passing_test(&mut self, test_passes: bool) -> &mut DummyBook { self.passing_test = test_passes; self } /// Write a book to a temporary directory using the provided settings. pub fn build(&self) -> Result { let temp = TempFileBuilder::new() .prefix("dummy_book-") .tempdir() .with_context(|| "Unable to create temp directory")?; let dummy_book_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/dummy_book"); recursive_copy(&dummy_book_root, temp.path()).with_context(|| { "Couldn't copy files into a \ temporary directory" })?; let sub_pattern = if self.passing_test { "true" } else { "false" }; let files_containing_tests = [ "src/first/nested.md", "src/first/nested-test.rs", "src/first/nested-test-with-anchors.rs", "src/first/partially-included-test.rs", "src/first/partially-included-test-with-anchors.rs", ]; for file in &files_containing_tests { let path_containing_tests = temp.path().join(file); replace_pattern_in_file(&path_containing_tests, "$TEST_STATUS", sub_pattern)?; } Ok(temp) } } fn replace_pattern_in_file(filename: &Path, from: &str, to: &str) -> Result<()> { let contents = fs::read_to_string(filename)?; File::create(filename)?.write_all(contents.replace(from, to).as_bytes())?; Ok(()) } /// Read the contents of the provided file into memory and then iterate through /// the list of strings asserting that the file contains all of them. pub fn assert_contains_strings>(filename: P, strings: &[&str]) { let filename = filename.as_ref(); let content = fs::read_to_string(filename).expect("Couldn't read the file's contents"); for s in strings { assert!( content.contains(s), "Searching for {:?} in {}\n\n{}", s, filename.display(), content ); } } pub fn assert_doesnt_contain_strings>(filename: P, strings: &[&str]) { let filename = filename.as_ref(); let content = fs::read_to_string(filename).expect("Couldn't read the file's contents"); for s in strings { assert!( !content.contains(s), "Found {:?} in {}\n\n{}", s, filename.display(), content ); } } /// Recursively copy an entire directory tree to somewhere else (a la `cp -r`). fn recursive_copy, B: AsRef>(from: A, to: B) -> Result<()> { let from = from.as_ref(); let to = to.as_ref(); for entry in WalkDir::new(&from) { let entry = entry.with_context(|| "Unable to inspect directory entry")?; let original_location = entry.path(); let relative = original_location .strip_prefix(&from) .expect("`original_location` is inside the `from` directory"); let new_location = to.join(relative); if original_location.is_file() { if let Some(parent) = new_location.parent() { fs::create_dir_all(parent).with_context(|| "Couldn't create directory")?; } fs::copy(&original_location, &new_location) .with_context(|| "Unable to copy file contents")?; } } Ok(()) } pub fn new_copy_of_example_book() -> Result { let temp = TempFileBuilder::new().prefix("guide").tempdir()?; let guide = Path::new(env!("CARGO_MANIFEST_DIR")).join("guide"); recursive_copy(guide, temp.path())?; Ok(temp) }