Skip to content

Commit 8614d16

Browse files
committed
extra: factor ConsoleTestState methods into an impl, fix perf bug.
It was re-reading terminfo on each line of output.
1 parent bf1f69c commit 8614d16

File tree

1 file changed

+148
-139
lines changed

1 file changed

+148
-139
lines changed

src/libextra/test.rs

Lines changed: 148 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ use std::result;
3131
use std::task;
3232
use std::to_str::ToStr;
3333
use std::u64;
34-
use std::uint;
3534

3635

3736
// The name of a test. By convention this follows the rules for rust
@@ -191,6 +190,7 @@ pub enum TestResult { TrOk, TrFailed, TrIgnored, TrBench(BenchSamples) }
191190
struct ConsoleTestState {
192191
out: @io::Writer,
193192
log_out: Option<@io::Writer>,
193+
term: Option<term::Terminal>,
194194
use_color: bool,
195195
total: uint,
196196
passed: uint,
@@ -200,171 +200,180 @@ struct ConsoleTestState {
200200
failures: ~[TestDesc]
201201
}
202202

203-
// A simple console test runner
204-
pub fn run_tests_console(opts: &TestOpts,
205-
tests: ~[TestDescAndFn]) -> bool {
206-
fn callback(event: &TestEvent, st: &mut ConsoleTestState) {
207-
debug!("callback(event=%?)", event);
208-
match copy *event {
209-
TeFiltered(ref filtered_tests) => {
210-
st.total = filtered_tests.len();
211-
let noun = if st.total != 1 { ~"tests" } else { ~"test" };
212-
st.out.write_line(fmt!("\nrunning %u %s", st.total, noun));
213-
}
214-
TeWait(ref test) => st.out.write_str(
215-
fmt!("test %s ... ", test.name.to_str())),
216-
TeResult(test, result) => {
217-
match st.log_out {
218-
Some(f) => write_log(f, copy result, &test),
219-
None => ()
220-
}
221-
match result {
222-
TrOk => {
223-
st.passed += 1;
224-
write_ok(st.out, st.use_color);
225-
st.out.write_line("");
226-
}
227-
TrFailed => {
228-
st.failed += 1;
229-
write_failed(st.out, st.use_color);
230-
st.out.write_line("");
231-
st.failures.push(test);
232-
}
233-
TrIgnored => {
234-
st.ignored += 1;
235-
write_ignored(st.out, st.use_color);
236-
st.out.write_line("");
237-
}
238-
TrBench(bs) => {
239-
st.benchmarked += 1u;
240-
write_bench(st.out, st.use_color);
241-
st.out.write_line(fmt!(": %s",
242-
fmt_bench_samples(&bs)));
243-
}
244-
}
245-
}
203+
impl ConsoleTestState {
204+
pub fn new(opts: &TestOpts) -> ConsoleTestState {
205+
let log_out = match opts.logfile {
206+
Some(ref path) => match io::file_writer(path,
207+
[io::Create,
208+
io::Truncate]) {
209+
result::Ok(w) => Some(w),
210+
result::Err(ref s) => {
211+
fail!("can't open output file: %s", *s)
212+
}
213+
},
214+
None => None
215+
};
216+
let out = io::stdout();
217+
let term = match term::Terminal::new(out) {
218+
Err(_) => None,
219+
Ok(t) => Some(t)
220+
};
221+
ConsoleTestState {
222+
out: out,
223+
log_out: log_out,
224+
use_color: use_color(),
225+
term: term,
226+
total: 0u,
227+
passed: 0u,
228+
failed: 0u,
229+
ignored: 0u,
230+
benchmarked: 0u,
231+
failures: ~[]
246232
}
247233
}
248234

249-
let log_out = match opts.logfile {
250-
Some(ref path) => match io::file_writer(path,
251-
[io::Create,
252-
io::Truncate]) {
253-
result::Ok(w) => Some(w),
254-
result::Err(ref s) => {
255-
fail!("can't open output file: %s", *s)
256-
}
257-
},
258-
None => None
259-
};
260-
261-
let st = @mut ConsoleTestState {
262-
out: io::stdout(),
263-
log_out: log_out,
264-
use_color: use_color(),
265-
total: 0u,
266-
passed: 0u,
267-
failed: 0u,
268-
ignored: 0u,
269-
benchmarked: 0u,
270-
failures: ~[]
271-
};
272-
273-
run_tests(opts, tests, |x| callback(&x, st));
274-
275-
assert!(st.passed + st.failed +
276-
st.ignored + st.benchmarked == st.total);
277-
let success = st.failed == 0u;
235+
pub fn write_ok(&self) {
236+
self.write_pretty("ok", term::color::GREEN);
237+
}
278238

279-
if !success {
280-
print_failures(st);
239+
pub fn write_failed(&self) {
240+
self.write_pretty("FAILED", term::color::RED);
281241
}
282242

283-
{
284-
let st: &mut ConsoleTestState = st;
285-
st.out.write_str(fmt!("\nresult: "));
286-
if success {
287-
// There's no parallelism at this point so it's safe to use color
288-
write_ok(st.out, true);
289-
} else {
290-
write_failed(st.out, true);
291-
}
292-
st.out.write_str(fmt!(". %u passed; %u failed; %u ignored\n\n",
293-
st.passed, st.failed, st.ignored));
243+
pub fn write_ignored(&self) {
244+
self.write_pretty("ignored", term::color::YELLOW);
294245
}
295246

296-
return success;
247+
pub fn write_bench(&self) {
248+
self.write_pretty("bench", term::color::CYAN);
249+
}
297250

298-
fn fmt_bench_samples(bs: &BenchSamples) -> ~str {
299-
if bs.mb_s != 0 {
300-
fmt!("%u ns/iter (+/- %u) = %u MB/s",
301-
bs.ns_iter_summ.median as uint,
302-
(bs.ns_iter_summ.max - bs.ns_iter_summ.min) as uint,
303-
bs.mb_s)
304-
} else {
305-
fmt!("%u ns/iter (+/- %u)",
306-
bs.ns_iter_summ.median as uint,
307-
(bs.ns_iter_summ.max - bs.ns_iter_summ.min) as uint)
251+
pub fn write_pretty(&self,
252+
word: &str,
253+
color: term::color::Color) {
254+
match self.term {
255+
None => self.out.write_str(word),
256+
Some(ref t) => {
257+
if self.use_color {
258+
t.fg(color);
259+
}
260+
self.out.write_str(word);
261+
if self.use_color {
262+
t.reset();
263+
}
264+
}
308265
}
309266
}
310267

311-
fn write_log(out: @io::Writer, result: TestResult, test: &TestDesc) {
312-
out.write_line(fmt!("%s %s",
313-
match result {
314-
TrOk => ~"ok",
315-
TrFailed => ~"failed",
316-
TrIgnored => ~"ignored",
317-
TrBench(ref bs) => fmt_bench_samples(bs)
318-
}, test.name.to_str()));
268+
pub fn write_run_start(&mut self, len: uint) {
269+
self.total = len;
270+
let noun = if len != 1 { &"tests" } else { &"test" };
271+
self.out.write_line(fmt!("\nrunning %u %s", len, noun));
319272
}
320273

321-
fn write_ok(out: @io::Writer, use_color: bool) {
322-
write_pretty(out, "ok", term::color::GREEN, use_color);
274+
pub fn write_test_start(&self, test: &TestDesc) {
275+
self.out.write_str(fmt!("test %s ... ", test.name.to_str()));
323276
}
324277

325-
fn write_failed(out: @io::Writer, use_color: bool) {
326-
write_pretty(out, "FAILED", term::color::RED, use_color);
278+
pub fn write_result(&self, result: &TestResult) {
279+
match *result {
280+
TrOk => self.write_ok(),
281+
TrFailed => self.write_failed(),
282+
TrIgnored => self.write_ignored(),
283+
TrBench(ref bs) => {
284+
self.write_bench();
285+
self.out.write_str(": " + fmt_bench_samples(bs))
286+
}
287+
}
288+
self.out.write_str(&"\n");
289+
}
290+
291+
pub fn write_log(&self, test: &TestDesc, result: &TestResult) {
292+
match self.log_out {
293+
None => (),
294+
Some(out) => {
295+
out.write_line(fmt!("%s %s",
296+
match *result {
297+
TrOk => ~"ok",
298+
TrFailed => ~"failed",
299+
TrIgnored => ~"ignored",
300+
TrBench(ref bs) => fmt_bench_samples(bs)
301+
}, test.name.to_str()));
302+
}
303+
}
327304
}
328305
329-
fn write_ignored(out: @io::Writer, use_color: bool) {
330-
write_pretty(out, "ignored", term::color::YELLOW, use_color);
306+
pub fn write_failures(&self) {
307+
self.out.write_line("\nfailures:");
308+
let mut failures = ~[];
309+
for self.failures.iter().advance() |f| {
310+
failures.push(f.name.to_str());
311+
}
312+
sort::tim_sort(failures);
313+
for failures.iter().advance |name| {
314+
self.out.write_line(fmt!(" %s", name.to_str()));
315+
}
331316
}
332317
333-
fn write_bench(out: @io::Writer, use_color: bool) {
334-
write_pretty(out, "bench", term::color::CYAN, use_color);
335-
}
318+
pub fn write_run_finish(&self) -> bool {
319+
assert!(self.passed + self.failed + self.ignored + self.benchmarked == self.total);
320+
let success = self.failed == 0u;
321+
if !success {
322+
self.write_failures();
323+
}
336324
337-
fn write_pretty(out: @io::Writer,
338-
word: &str,
339-
color: term::color::Color,
340-
use_color: bool) {
341-
let t = term::Terminal::new(out);
342-
match t {
343-
Ok(term) => {
344-
if use_color {
345-
term.fg(color);
346-
}
347-
out.write_str(word);
348-
if use_color {
349-
term.reset();
350-
}
351-
},
352-
Err(_) => out.write_str(word)
325+
self.out.write_str("\nresult: ");
326+
if success {
327+
// There's no parallelism at this point so it's safe to use color
328+
self.write_ok();
329+
} else {
330+
self.write_failed();
353331
}
332+
self.out.write_str(fmt!(". %u passed; %u failed; %u ignored, %u benchmarked\n\n",
333+
self.passed, self.failed, self.ignored, self.benchmarked));
334+
return success;
354335
}
355336
}
356337

357-
fn print_failures(st: &ConsoleTestState) {
358-
st.out.write_line("\nfailures:");
359-
let mut failures = ~[];
360-
for uint::range(0, st.failures.len()) |i| {
361-
let name = copy st.failures[i].name;
362-
failures.push(name.to_str());
338+
pub fn fmt_bench_samples(bs: &BenchSamples) -> ~str {
339+
if bs.mb_s != 0 {
340+
fmt!("%u ns/iter (+/- %u) = %u MB/s",
341+
bs.ns_iter_summ.median as uint,
342+
(bs.ns_iter_summ.max - bs.ns_iter_summ.min) as uint,
343+
bs.mb_s)
344+
} else {
345+
fmt!("%u ns/iter (+/- %u)",
346+
bs.ns_iter_summ.median as uint,
347+
(bs.ns_iter_summ.max - bs.ns_iter_summ.min) as uint)
363348
}
364-
sort::tim_sort(failures);
365-
for failures.iter().advance |name| {
366-
st.out.write_line(fmt!(" %s", name.to_str()));
349+
}
350+
351+
// A simple console test runner
352+
pub fn run_tests_console(opts: &TestOpts,
353+
tests: ~[TestDescAndFn]) -> bool {
354+
fn callback(event: &TestEvent, st: &mut ConsoleTestState) {
355+
debug!("callback(event=%?)", event);
356+
match copy *event {
357+
TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
358+
TeWait(ref test) => st.write_test_start(test),
359+
TeResult(test, result) => {
360+
st.write_log(&test, &result);
361+
st.write_result(&result);
362+
match result {
363+
TrOk => st.passed += 1,
364+
TrIgnored => st.ignored += 1,
365+
TrBench(_) => st.benchmarked += 1,
366+
TrFailed => {
367+
st.failed += 1;
368+
st.failures.push(test);
369+
}
370+
}
371+
}
372+
}
367373
}
374+
let st = @mut ConsoleTestState::new(opts);
375+
run_tests(opts, tests, |x| callback(&x, st));
376+
return st.write_run_finish();
368377
}
369378

370379
#[test]

0 commit comments

Comments
 (0)