Skip to content

Commit 852d977

Browse files
author
Dylan McKay
committed
Import files from 'compiler' project
0 parents  commit 852d977

File tree

8 files changed

+736
-0
lines changed

8 files changed

+736
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
target
2+
Cargo.lock

Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "lit"
3+
version = "0.1.0"
4+
authors = ["Dylan McKay <dylanmckay34@gmail.com>"]
5+
6+
[dependencies]
7+
walkdir = "0.1"
8+
term = "*"
9+
regex = "0.1.71"
10+

src/find.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use std;
2+
use walkdir::WalkDir;
3+
4+
/// Recursively finds tests for the given paths.
5+
pub fn in_paths<'a,P>(paths: P) -> Result<Vec<String>,String>
6+
where P: IntoIterator<Item=&'a str> {
7+
let mut tests = Vec::new();
8+
9+
for path in paths.into_iter() {
10+
let path_tests = try!(in_path(path));
11+
tests.extend(path_tests.into_iter());
12+
}
13+
14+
Ok(tests)
15+
}
16+
17+
pub fn in_path(path: &str)
18+
-> Result<Vec<String>,String> {
19+
let metadata = match std::fs::metadata(path) {
20+
Ok(meta) => meta,
21+
Err(e) => return Err(format!("failed to open '{}': {}",
22+
path, e)),
23+
};
24+
25+
if metadata.is_dir() {
26+
find_tests_in_dir(path)
27+
} else {
28+
Ok(vec![path.to_owned()])
29+
}
30+
}
31+
32+
fn find_tests_in_dir(path: &str) -> Result<Vec<String>,String> {
33+
let tests = try!(find_files_in_dir(path)).into_iter()
34+
.filter(|f| f.ends_with(".ir"))
35+
.collect();
36+
Ok(tests)
37+
}
38+
39+
fn find_files_in_dir(path: &str) -> Result<Vec<String>,String> {
40+
let mut dir_tests = Vec::new();
41+
42+
for entry in WalkDir::new(path) {
43+
let entry = entry.unwrap();
44+
45+
// don't go into an infinite loop
46+
if entry.path().to_str().unwrap() == path {
47+
continue;
48+
}
49+
50+
if entry.metadata().unwrap().is_file() {
51+
dir_tests.push(entry.path().to_str().unwrap().to_owned());
52+
}
53+
}
54+
55+
Ok(dir_tests)
56+
}
57+

src/instance.rs

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
use {Context, Test, Directive, Command, TestResultKind};
2+
use std::process;
3+
use std::collections::HashMap;
4+
use regex::Regex;
5+
6+
use tool;
7+
use std;
8+
9+
pub struct Instance
10+
{
11+
pub invocation: tool::Invocation,
12+
}
13+
14+
impl Instance
15+
{
16+
pub fn new(invocation: tool::Invocation) -> Self {
17+
Instance { invocation: invocation }
18+
}
19+
20+
pub fn run(self, test: &Test, context: &Context) -> TestResultKind {
21+
let exe_path = context.executable_path(&self.invocation.executable);
22+
let mut cmd = self.build_command(test, context);
23+
24+
let output = match cmd.output() {
25+
Ok(o) => o,
26+
Err(e) => match e.kind() {
27+
std::io::ErrorKind::NotFound => {
28+
return TestResultKind::Fail(
29+
format!("executable not found: {}",
30+
exe_path), "".to_owned());
31+
},
32+
_ => {
33+
return TestResultKind::Fail(
34+
format!("could not execute: '{}', {}",
35+
exe_path, e), "".to_owned());
36+
},
37+
},
38+
};
39+
40+
if !output.status.success() {
41+
let stderr = String::from_utf8(output.stderr).unwrap();
42+
43+
return TestResultKind::Fail(format!(
44+
"{} exited with code {}", exe_path,
45+
output.status.code().unwrap()),
46+
stderr
47+
);
48+
}
49+
50+
let stdout = String::from_utf8(output.stdout).unwrap();
51+
52+
let stdout_lines: Vec<_> = stdout.lines().map(|l| l.trim().to_owned()).collect();
53+
let stdout: String = stdout_lines.join("\n");
54+
55+
Checker::new(stdout).run(&test)
56+
}
57+
58+
pub fn build_command(&self, test: &Test, context: &Context) -> process::Command {
59+
let exe_path = context.executable_path(&self.invocation.executable);
60+
let mut cmd = process::Command::new(&exe_path);
61+
62+
for arg in self.invocation.arguments.iter() {
63+
let arg_str = arg.resolve(test);
64+
cmd.arg(arg_str);
65+
}
66+
67+
cmd
68+
}
69+
}
70+
71+
struct Checker
72+
{
73+
stdout: String,
74+
variables: HashMap<String, String>,
75+
}
76+
77+
impl Checker
78+
{
79+
fn new(stdout: String) -> Self {
80+
Checker {
81+
stdout: stdout,
82+
variables: HashMap::new(),
83+
}
84+
}
85+
86+
fn run(&mut self, test: &Test) -> TestResultKind {
87+
for directive in test.directives.iter() {
88+
match directive.command {
89+
Command::Run(..) => (),
90+
Command::Check(ref regex) => {
91+
let regex = self.resolve_variables(regex.clone());
92+
93+
let beginning_line = match self.stdout.lines().next() {
94+
Some(l) => l.to_owned(),
95+
None => return TestResultKind::fail(
96+
format_check_error(test, directive, "reached end of file", "")
97+
),
98+
};
99+
100+
let mut matched_line = None;
101+
let tmp: Vec<_> = self.stdout.lines().map(|l| l.to_owned()).skip_while(|line| {
102+
if regex.is_match(&line) {
103+
matched_line = Some(line.to_owned());
104+
false // stop processing lines
105+
} else {
106+
true
107+
}
108+
}).skip(1).collect();
109+
self.stdout = tmp.join("\n");
110+
111+
if let Some(matched_line) = matched_line {
112+
self.process_captures(&regex, &matched_line);
113+
} else {
114+
return TestResultKind::fail(
115+
format_check_error(test,
116+
directive,
117+
&format!("could not find match: '{}'", regex),
118+
&beginning_line,
119+
)
120+
);
121+
}
122+
},
123+
Command::CheckNext(ref regex) => {
124+
let regex = self.resolve_variables(regex.clone());
125+
126+
if let Some(ref next_line) = self.stdout.lines().next().map(|l| l.to_owned()) {
127+
if regex.is_match(&next_line) {
128+
let lines: Vec<_> = self.stdout.lines().skip(1).map(|l| l.to_owned()).collect();
129+
self.stdout = lines.join("\n");
130+
131+
self.process_captures(&regex, &next_line);
132+
} else {
133+
return TestResultKind::fail(
134+
format_check_error(test,
135+
directive,
136+
&format!("could not find match: '{}'", regex),
137+
&next_line)
138+
);
139+
}
140+
} else {
141+
return TestResultKind::fail(format!("check-next reached the end of file"));
142+
}
143+
},
144+
}
145+
}
146+
147+
TestResultKind::Pass
148+
}
149+
150+
pub fn process_captures(&mut self, regex: &Regex, line: &str) {
151+
// We shouldn't be calling this function if it didn't match.
152+
debug_assert_eq!(regex.is_match(line), true);
153+
let captures = if let Some(captures) = regex.captures(line) {
154+
captures
155+
} else {
156+
return;
157+
};
158+
159+
for capture_name in regex.capture_names() {
160+
// we only care about named captures.
161+
if let Some(name) = capture_name {
162+
let captured_value = captures.name(name).unwrap();
163+
164+
self.variables.insert(name.to_owned(), captured_value.to_owned());
165+
}
166+
}
167+
}
168+
169+
pub fn resolve_variables(&self, mut regex: Regex) -> Regex {
170+
for (name, value) in self.variables.iter() {
171+
let subst_expr = format!("[[{}]]", name);
172+
let regex_str = format!("{}", regex);
173+
let regex_str = regex_str.replace(&subst_expr, value);
174+
regex = Regex::new(&regex_str).unwrap();
175+
}
176+
177+
regex
178+
}
179+
}
180+
181+
fn format_check_error(test: &Test,
182+
directive: &Directive,
183+
msg: &str,
184+
next_line: &str) -> String {
185+
self::format_error(test, directive, msg, next_line)
186+
}
187+
188+
fn format_error(test: &Test,
189+
directive: &Directive,
190+
msg: &str,
191+
next_line: &str) -> String {
192+
format!("{}:{}: {}\nnext line: '{}'", test.path, directive.line, msg, next_line)
193+
}
194+

src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
pub use self::tool::*;
2+
pub use self::test::*;
3+
pub use self::instance::Instance;
4+
5+
pub mod tool;
6+
pub mod test;
7+
pub mod find;
8+
pub mod print;
9+
pub mod instance;
10+
11+
extern crate walkdir;
12+
extern crate term;
13+
extern crate regex;

src/print.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use {TestResult,TestResultKind};
2+
3+
use std;
4+
use term;
5+
6+
pub fn result(result: &TestResult) {
7+
match result.kind {
8+
TestResultKind::Pass => {
9+
success(format!("PASS :: {}", result.path));
10+
},
11+
TestResultKind::Skip => {
12+
line();
13+
warning(format!(
14+
"SKIP :: {} (test does not contain any directives)",
15+
result.path));
16+
line();
17+
},
18+
TestResultKind::Fail(ref msg, ref desc) => {
19+
line();
20+
21+
failure(format!("FAIL :: {}", result.path));
22+
text(msg.clone());
23+
24+
// Only print stderr if there was output
25+
if !desc.is_empty() {
26+
line();
27+
text("stderr:");
28+
line();
29+
text(desc.clone());
30+
}
31+
32+
line();
33+
},
34+
}
35+
}
36+
37+
38+
pub fn line() {
39+
with("\n",
40+
term::stdout().unwrap(),
41+
term::color::WHITE);
42+
}
43+
44+
pub fn text<S>(msg: S)
45+
where S: Into<String> {
46+
with(format!("{}\n", msg.into()),
47+
term::stdout().unwrap(),
48+
term::color::WHITE);
49+
}
50+
51+
pub fn success<S>(msg: S)
52+
where S: Into<String> {
53+
with(format!("{}\n", msg.into()),
54+
term::stdout().unwrap(),
55+
term::color::GREEN);
56+
}
57+
58+
pub fn warning<S>(msg: S)
59+
where S: Into<String> {
60+
with(format!("{}\n", msg.into()),
61+
term::stderr().unwrap(),
62+
term::color::YELLOW);
63+
}
64+
65+
pub fn failure<S>(msg: S)
66+
where S: Into<String> {
67+
with(format!("{}\n", msg.into()),
68+
term::stderr().unwrap(),
69+
term::color::RED);
70+
}
71+
72+
pub fn with<S,W>(msg: S,
73+
mut term: Box<term::Terminal<Output=W>>,
74+
color: term::color::Color)
75+
where S: Into<String>, W: std::io::Write {
76+
77+
term.fg(color).unwrap();
78+
write!(term, "{}", msg.into()).unwrap();
79+
}

0 commit comments

Comments
 (0)