Skip to content

Commit

Permalink
Merge pull request #311 from baszalmstra/refactor/book/tests
Browse files Browse the repository at this point in the history
refactor: generate rust tests for code snippets in book
  • Loading branch information
baszalmstra authored Apr 10, 2021
2 parents aece59e + 7304496 commit aaa677a
Show file tree
Hide file tree
Showing 19 changed files with 559 additions and 231 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,17 @@ jobs:
uses: actions-rs/install@v0.1
with:
crate: mdbook
version: latest
version: 0.4.7
use-tool-cache: true

- name: mdbook test
if: ${{ matrix.config.os == 'ubuntu-latest' }}
run: mdbook test book -L target/debug/deps

- name: mdbook build
if: ${{ matrix.config.os == 'ubuntu-latest' }}
run: mdbook build book

- name: Cargo test
uses: actions-rs/cargo@v1
with:
Expand Down
3 changes: 0 additions & 3 deletions book/book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,3 @@ title = "The Mun Programming Language"
additional-css = ["theme/mun.css"]
additional-js = ["theme/trailing_slash_hack.js"]
git-repository-url = "https://github.com/mun-lang/mun/tree/master/book"

[output.mun_mdbook_test_plugin]
command = "cargo run --package mun_mdbook_test_plugin --"
6 changes: 1 addition & 5 deletions book/ci/build
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,13 @@ pushd ./book
mdbook="mdbook"
if ! [ -x "$(command -v $mdbook)" ]; then
echo "Installing mdbook.."
curl -sL https://github.com/rust-lang-nursery/mdBook/releases/download/v0.3.7/mdbook-v0.3.7-x86_64-unknown-linux-gnu.tar.gz | tar zxv
curl -sL https://github.com/rust-lang-nursery/mdBook/releases/download/v0.4.7/mdbook-v0.4.7-x86_64-unknown-linux-gnu.tar.gz | tar zxv
mdbook="./mdbook"
fi

# Echo mdbook version
$mdbook --version

# Comment out everything that has to do with the test plugin. We don't want to run it when
# generating the book
sed -e '/.*mun_mdbook_test_plugin.*/ s/^#*/#/' -i ./book.toml

# First build our custom highlight.js
./ci/build-highlight-js

Expand Down
4 changes: 4 additions & 0 deletions crates/mun/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ features = ["precommit-hook", "run-cargo-test", "run-cargo-fmt", "run-cargo-clip

[dev-dependencies]
tempfile = "3.1"
mun_skeptic = { path = "../mun_skeptic" }

[build-dependencies]
mun_skeptic = { path = "../mun_skeptic" }
3 changes: 3 additions & 0 deletions crates/mun/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
mun_skeptic::generate_doc_tests_from_mdbook("../../book");
}
54 changes: 38 additions & 16 deletions crates/mun/src/ops/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,25 @@ use mun_project::MANIFEST_FILENAME;

use crate::ExitStatus;

/// Options for building Mun code
struct BuildOptions {
manifest_path: Option<String>,
display_colors: DisplayColor,
compiler_options: Config,
}

/// This method is invoked when the executable is run with the `build` argument indicating that a
/// user requested us to build a project in the current directory or one of its parent directories.
pub fn build(matches: &ArgMatches) -> Result<ExitStatus, anyhow::Error> {
log::trace!("starting build");

let options = compiler_options(matches)?;
let options = extract_build_options(matches)?;

// Locate the manifest
let manifest_path = match matches.value_of("manifest-path") {
let manifest_path = match options.manifest_path {
None => {
let current_dir =
std::env::current_dir().expect("could not determine currrent working directory");
std::env::current_dir().expect("could not determine current working directory");
find_manifest(&current_dir).ok_or_else(|| {
anyhow::anyhow!(
"could not find {} in '{}' or a parent directory",
Expand All @@ -28,16 +35,24 @@ pub fn build(matches: &ArgMatches) -> Result<ExitStatus, anyhow::Error> {
)
})?
}
Some(path) => std::fs::canonicalize(Path::new(path))
Some(path) => std::fs::canonicalize(Path::new(&path))
.map_err(|_| anyhow::anyhow!("'{}' does not refer to a valid manifest path", path))?,
};

log::info!("located build manifest at: {}", manifest_path.display());

if matches.is_present("watch") {
mun_compiler_daemon::compile_and_watch_manifest(&manifest_path, options)
mun_compiler_daemon::compile_and_watch_manifest(
&manifest_path,
options.compiler_options,
options.display_colors,
)
} else {
mun_compiler::compile_manifest(&manifest_path, options)
mun_compiler::compile_manifest(
&manifest_path,
options.compiler_options,
options.display_colors,
)
}
.map(Into::into)
}
Expand All @@ -55,7 +70,8 @@ fn find_manifest(directory: &Path) -> Option<PathBuf> {
None
}

fn compiler_options(matches: &ArgMatches) -> Result<mun_compiler::Config, anyhow::Error> {
/// Extract build options from the command line
fn extract_build_options(matches: &ArgMatches) -> Result<BuildOptions, anyhow::Error> {
let optimization_lvl = match matches.value_of("opt-level") {
Some("0") => mun_compiler::OptimizationLevel::None,
Some("1") => mun_compiler::OptimizationLevel::Less,
Expand All @@ -64,7 +80,7 @@ fn compiler_options(matches: &ArgMatches) -> Result<mun_compiler::Config, anyhow
_ => return Err(anyhow!("Only optimization levels 0-3 are supported")),
};

let display_color = matches
let display_colors = matches
.value_of("color")
.map(ToOwned::to_owned)
.or_else(|| env::var("MUN_TERMINAL_COLOR").ok())
Expand All @@ -77,14 +93,20 @@ fn compiler_options(matches: &ArgMatches) -> Result<mun_compiler::Config, anyhow

let emit_ir = matches.is_present("emit-ir");

Ok(Config {
target: matches
.value_of("target")
.map_or_else(Target::host_target, Target::search)?,
optimization_lvl,
out_dir: None,
display_color,
emit_ir,
let manifest_path = matches.value_of("manifest-path").map(ToOwned::to_owned);

Ok(BuildOptions {
manifest_path,
display_colors,
compiler_options: Config {
target: matches
.value_of("target")
.map_or_else(Target::host_target, Target::search)?,
optimization_lvl,
out_dir: None,

emit_ir,
},
})
}

Expand Down
1 change: 1 addition & 0 deletions crates/mun/tests/book.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include!(concat!(env!("OUT_DIR"), "/skeptic-tests.rs"));
10 changes: 5 additions & 5 deletions crates/mun_compiler/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ mod tests {

/// Compile passed source code and return all compilation errors
fn compilation_errors(source_code: &str) -> String {
let config = Config {
display_color: DisplayColor::Disable,
..Config::default()
};
let config = Config::default();

let input = PathOrInline::Inline {
rel_path: RelativePathBuf::from("main.mun"),
Expand All @@ -20,7 +17,10 @@ mod tests {
let mut compilation_errors = Vec::<u8>::new();

let _ = driver
.emit_diagnostics(&mut Cursor::new(&mut compilation_errors))
.emit_diagnostics(
&mut Cursor::new(&mut compilation_errors),
DisplayColor::Disable,
)
.unwrap();

String::from_utf8(compilation_errors).unwrap()
Expand Down
69 changes: 45 additions & 24 deletions crates/mun_compiler/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ use hir::{
use mun_codegen::{AssemblyIR, CodeGenDatabase, ModuleGroup, TargetAssembly};
use paths::RelativePathBuf;

use std::{path::PathBuf, sync::Arc};

mod config;
mod display_color;

Expand All @@ -22,7 +20,10 @@ pub use self::display_color::DisplayColor;

use crate::diagnostics_snippets::{emit_hir_diagnostic, emit_syntax_error};
use mun_project::{Package, LOCKFILE_NAME};
use std::{collections::HashMap, convert::TryInto, path::Path, time::Duration};
use std::{
collections::HashMap, convert::TryInto, io::Cursor, path::Path, path::PathBuf, sync::Arc,
time::Duration,
};
use walkdir::WalkDir;

pub const WORKSPACE: SourceRootId = SourceRootId(0);
Expand All @@ -38,36 +39,31 @@ pub struct Driver {

module_to_temp_assembly_path: HashMap<Module, PathBuf>,

display_color: DisplayColor,
emit_ir: bool,
}

impl Driver {
/// Constructs a driver with a specific configuration.
pub fn with_config(config: Config, out_dir: PathBuf) -> Result<Self, anyhow::Error> {
Ok(Self {
pub fn with_config(config: Config, out_dir: PathBuf) -> Self {
Self {
db: CompilerDatabase::new(&config),
out_dir,
source_root: Default::default(),
path_to_file_id: Default::default(),
file_id_to_path: Default::default(),
next_file_id: 0,
module_to_temp_assembly_path: Default::default(),
display_color: config.display_color,
emit_ir: config.emit_ir,
})
}
}

/// Constructs a driver with a configuration and a single file.
pub fn with_file(
config: Config,
path: PathOrInline,
) -> Result<(Driver, FileId), anyhow::Error> {
pub fn with_file(config: Config, path: PathOrInline) -> anyhow::Result<(Driver, FileId)> {
let out_dir = config.out_dir.clone().unwrap_or_else(|| {
std::env::current_dir().expect("could not determine current working directory")
});

let mut driver = Driver::with_config(config, out_dir)?;
let mut driver = Driver::with_config(config, out_dir);

// Get the path and contents of the path
let (rel_path, text) = match path {
Expand Down Expand Up @@ -120,7 +116,7 @@ impl Driver {
.map_err(|e| anyhow::anyhow!("could not create package output directory: {}", e))?;

// Construct the driver
let mut driver = Driver::with_config(config, output_dir)?;
let mut driver = Driver::with_config(config, output_dir);

// Iterate over all files in the source directory of the package and store their information in
// the database
Expand Down Expand Up @@ -216,8 +212,12 @@ impl Driver {
impl Driver {
/// Emits all diagnostic messages currently in the database; returns true if errors were
/// emitted.
pub fn emit_diagnostics(&self, writer: &mut dyn std::io::Write) -> Result<bool, anyhow::Error> {
let emit_colors = self.display_color.should_enable();
pub fn emit_diagnostics(
&self,
writer: &mut dyn std::io::Write,
display_color: DisplayColor,
) -> Result<bool, anyhow::Error> {
let emit_colors = display_color.should_enable();
let mut has_error = false;

for package in hir::Package::all(self.db.upcast()) {
Expand Down Expand Up @@ -265,6 +265,24 @@ impl Driver {

Ok(has_error)
}

/// Returns all diagnostics as a human readable string
pub fn emit_diagnostics_to_string(
&self,
display_color: DisplayColor,
) -> anyhow::Result<Option<String>> {
let mut compiler_errors: Vec<u8> = Vec::new();
if !self.emit_diagnostics(&mut Cursor::new(&mut compiler_errors), display_color)? {
Ok(None)
} else {
Ok(Some(String::from_utf8(compiler_errors).map_err(|e| {
anyhow::anyhow!(
"could not convert compiler diagnostics to valid UTF8: {}",
e
)
})?))
}
}
}

impl Driver {
Expand Down Expand Up @@ -340,14 +358,17 @@ impl Driver {
match lockfile::Lockfile::create(self.out_dir.join(LOCKFILE_NAME)) {
Ok(lockfile) => break lockfile,
Err(_) => {
if self.display_color.should_enable() {
eprintln!(
"{} on acquiring lock on output directory",
yansi_term::Color::Cyan.paint("Blocked")
)
} else {
eprintln!("Blocked on acquiring lock on output directory")
}
// TODO(#313): Implement/abstract a better way to emit warnings/errors from the
// driver. Directly writing to stdout accesses global state. The driver is not
// aware of how to output logging information.
// if self.display_color.should_enable() {
// eprintln!(
// "{} on acquiring lock on output directory",
// yansi_term::Color::Cyan.paint("Blocked")
// )
// } else {
// eprintln!("Blocked on acquiring lock on output directory")
// }
std::thread::sleep(Duration::from_secs(1))
}
};
Expand Down
5 changes: 0 additions & 5 deletions crates/mun_compiler/src/driver/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::DisplayColor;
pub use mun_codegen::OptimizationLevel;
use mun_target::spec::Target;
use std::path::PathBuf;
Expand All @@ -16,9 +15,6 @@ pub struct Config {
/// is stored in a temporary directory.
pub out_dir: Option<PathBuf>,

/// Whether or not to use colors in terminal output
pub display_color: DisplayColor,

/// Whether or not to emit an IR file instead of a munlib.
pub emit_ir: bool,
}
Expand All @@ -32,7 +28,6 @@ impl Default for Config {
target: target.unwrap(),
optimization_lvl: OptimizationLevel::Default,
out_dir: None,
display_color: DisplayColor::Auto,
emit_ir: false,
}
}
Expand Down
13 changes: 11 additions & 2 deletions crates/mun_compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,17 @@ pub struct CompilerOptions {

/// The compiler configuration
pub config: Config,

/// Wether or not to display colors on the command line
pub emit_colors: DisplayColor,
}

impl CompilerOptions {
pub fn with_path<P: AsRef<Path>>(input: P) -> CompilerOptions {
CompilerOptions {
input: PathOrInline::Path(input.as_ref().to_path_buf()),
config: Config::default(),
emit_colors: DisplayColor::Auto,
}
}

Expand All @@ -57,6 +61,7 @@ impl CompilerOptions {
contents: input.as_ref().to_string(),
},
config: Config::default(),
emit_colors: DisplayColor::Auto,
}
}
}
Expand All @@ -79,11 +84,15 @@ pub fn ensure_package_output_dir(
Ok(out_dir)
}

pub fn compile_manifest(manifest_path: &Path, config: Config) -> Result<bool, anyhow::Error> {
pub fn compile_manifest(
manifest_path: &Path,
config: Config,
emit_colors: DisplayColor,
) -> Result<bool, anyhow::Error> {
let (_package, mut driver) = Driver::with_package_path(manifest_path, config)?;

// Emit diagnostics. If one of the snippets is an error, abort gracefully.
if driver.emit_diagnostics(&mut stderr())? {
if driver.emit_diagnostics(&mut stderr(), emit_colors)? {
return Ok(false);
};

Expand Down
Loading

0 comments on commit aaa677a

Please sign in to comment.