Actually run recipes, add a bunch of tests

This commit is contained in:
Casey Rodarmor 2016-10-05 13:58:18 -07:00
parent 823b7a5181
commit c957165f4c
6 changed files with 119 additions and 10 deletions

19
Cargo.lock generated
View File

@ -4,6 +4,7 @@ version = "0.1.5"
dependencies = [
"clap 2.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.77 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -61,6 +62,14 @@ dependencies = [
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.1.77"
@ -83,6 +92,14 @@ name = "strsim"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "tempdir"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "term_size"
version = "0.2.1"
@ -148,9 +165,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d"
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
"checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5"
"checksum regex 0.1.77 (registry+https://github.com/rust-lang/crates.io-index)" = "64b03446c466d35b42f2a8b203c8e03ed8b91c0f17b56e1f84f7210a257aa665"
"checksum regex-syntax 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279401017ae31cf4e15344aa3f085d0e2e5c1e70067289ef906906fdbe92c8fd"
"checksum strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "50c069df92e4b01425a8bf3576d5d417943a6a7272fbabaf5bd80b1aaa76442e"
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
"checksum term_size 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f7f5f3f71b0040cecc71af239414c23fd3c73570f5ff54cf50e03cef637f2a0"
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"

View File

@ -10,3 +10,4 @@ homepage = "https://github.com/casey/j"
regex = "^0.1.77"
clap = "^2.0.0"
tempdir = "^0.3.5"

View File

@ -1,6 +1,6 @@
test:
cargo test --lib
cargo run -- quine
cargo run -- quine clean > /dev/null 2> /dev/null
# list all recipies
list:

5
notes
View File

@ -2,12 +2,11 @@ notes
-----
-- report double compile error
-- actually run recipes
-- actually parse recipe and validate contents
-- think about maybe using multiple cores
-- should pre-requisite order really be arbitrary?
-- test plan order
-- use multiple cores
- look through all justfiles for features of make that I use. so far:
. phony
. SHELL := zsh

View File

@ -5,7 +5,7 @@ extern crate regex;
use std::io::prelude::*;
use std::{fs, fmt};
use std::{fs, fmt, process, io};
use std::collections::{BTreeMap, BTreeSet, HashSet};
use std::fmt::Display;
use regex::Regex;
@ -49,14 +49,46 @@ struct Recipe<'a> {
dependencies: BTreeSet<&'a str>,
}
#[cfg(unix)]
fn error_from_signal<'a>(recipe: &'a str, exit_status: process::ExitStatus) -> RunError<'a> {
use std::os::unix::process::ExitStatusExt;
match exit_status.signal() {
Some(signal) => RunError::Signal{recipe: recipe, signal: signal},
None => RunError::UnknownFailure{recipe: recipe},
}
}
#[cfg(windows)]
fn error_from_signal<'a>(recipe: &'a str, exit_status: process::ExitStatus) -> RunError<'a> {
RunError::UnknownFailure{recipe: recipe}
}
impl<'a> Recipe<'a> {
fn run(&self) -> Result<(), RunError<'a>> {
// TODO: actually run recipes
warn!("running {}", self.name);
for command in &self.commands {
let mut command = *command;
if !command.starts_with("@") {
warn!("{}", command);
} else {
command = &command[1..];
}
let status = process::Command::new("sh")
.arg("-c")
.arg(command)
.status();
try!(match status {
Ok(exit_status) => if let Some(code) = exit_status.code() {
if code == 0 {
Ok(())
} else {
Err(RunError::Code{recipe: self.name, code: code})
}
} else {
Err(error_from_signal(self.name, exit_status))
},
Err(io_error) => Err(RunError::IoError{recipe: self.name, io_error: io_error})
});
}
// Err(RunError::Code{recipe: self.name, code: -1})
Ok(())
}
}
@ -270,10 +302,13 @@ impl<'a> Justfile<'a> {
}
}
#[derive(Debug)]
pub enum RunError<'a> {
UnknownRecipes{recipes: Vec<&'a str>},
// Signal{recipe: &'a str, signal: i32},
Signal{recipe: &'a str, signal: i32},
Code{recipe: &'a str, code: i32},
UnknownFailure{recipe: &'a str},
IoError{recipe: &'a str, io_error: io::Error},
}
impl<'a> Display for RunError<'a> {
@ -289,6 +324,19 @@ impl<'a> Display for RunError<'a> {
&RunError::Code{recipe, code} => {
try!(write!(f, "Recipe \"{}\" failed with code {}", recipe, code));
},
&RunError::Signal{recipe, signal} => {
try!(write!(f, "Recipe \"{}\" wast terminated by signal {}", recipe, signal));
}
&RunError::UnknownFailure{recipe} => {
try!(write!(f, "Recipe \"{}\" failed for an unknown reason", recipe));
},
&RunError::IoError{recipe, ref io_error} => {
try!(match io_error.kind() {
io::ErrorKind::NotFound => write!(f, "Recipe \"{}\" could not be run because j could not find `sh` the command:\n{}", recipe, io_error),
io::ErrorKind::PermissionDenied => write!(f, "Recipe \"{}\" could not be run because j could not run `sh`:\n{}", recipe, io_error),
_ => write!(f, "Recipe \"{}\" could not be run because of an IO error while launching the `sh`:\n{}", recipe, io_error),
});
},
}
Ok(())
}

View File

@ -1,3 +1,5 @@
extern crate tempdir;
use super::{ErrorKind, Justfile};
fn expect_error(text: &str, line: usize, expected_error_kind: ErrorKind) {
@ -130,3 +132,43 @@ fn first() {
let justfile = expect_success("#hello\n#goodbye\na:\nb:\nc:\n");
assert!(justfile.first().unwrap() == "a");
}
#[test]
fn unknown_recipes() {
match expect_success("a:\nb:\nc:").run(&["a", "x", "y", "z"]).unwrap_err() {
super::RunError::UnknownRecipes{recipes} => assert_eq!(recipes, &["x", "y", "z"]),
other @ _ => panic!("expected an unknown recipe error, but got: {}", other),
}
}
#[test]
fn code_error() {
match expect_success("fail:\n @function x { return 100; }; x").run(&["fail"]).unwrap_err() {
super::RunError::Code{recipe, code} => {
assert_eq!(recipe, "fail");
assert_eq!(code, 100);
},
other @ _ => panic!("expected an code run error, but got: {}", other),
}
}
#[test]
fn run_order() {
let tmp = tempdir::TempDir::new("run_order").unwrap_or_else(|err| panic!("tmpdir: failed to create temporary directory: {}", err));
let path = tmp.path().to_str().unwrap_or_else(|| panic!("tmpdir: path was not valid UTF-8")).to_owned();
let text = r"
a:
@touch a
b: a
@mv a b
c: b
@mv b c
d: c
@rm c
";
super::std::env::set_current_dir(path).expect("failed to set current directory");
expect_success(text).run(&["a", "d"]).unwrap();
}