Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: generate rust tests for code snippets in book #311

Merged
merged 1 commit into from
Apr 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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