diff --git a/.gitmodules b/.gitmodules index 39288a7ae49..86c5c780c5e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,6 +8,7 @@ [submodule "src/rt/hoedown"] path = src/rt/hoedown url = https://github.com/rust-lang/hoedown.git + branch = rust-2015-09-21-do-not-delete [submodule "src/jemalloc"] path = src/jemalloc url = https://github.com/rust-lang/jemalloc.git diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index cdb24a56367..fe2edcad25c 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -521,17 +521,22 @@ impl<'a, I: IntoIterator> NestedAttributesExt for #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug, Default)] pub struct Attributes { pub doc_strings: Vec, - pub other_attrs: Vec + pub other_attrs: Vec, + pub span: Option, } impl Attributes { pub fn from_ast(attrs: &[ast::Attribute]) -> Attributes { let mut doc_strings = vec![]; + let mut sp = None; let other_attrs = attrs.iter().filter_map(|attr| { attr.with_desugared_doc(|attr| { if let Some(value) = attr.value_str() { if attr.check_name("doc") { doc_strings.push(value.to_string()); + if sp.is_none() { + sp = Some(attr.span); + } return None; } } @@ -541,7 +546,8 @@ impl Attributes { }).collect(); Attributes { doc_strings: doc_strings, - other_attrs: other_attrs + other_attrs: other_attrs, + span: sp, } } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 442a2f40742..e8ff8930bdd 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -71,29 +71,31 @@ const HOEDOWN_EXTENSIONS: libc::c_uint = enum hoedown_document {} type blockcodefn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer, - *const hoedown_buffer, *const hoedown_renderer_data); + *const hoedown_buffer, *const hoedown_renderer_data, + libc::size_t); type blockquotefn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer, - *const hoedown_renderer_data); + *const hoedown_renderer_data, libc::size_t); type headerfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer, - libc::c_int, *const hoedown_renderer_data); + libc::c_int, *const hoedown_renderer_data, + libc::size_t); type blockhtmlfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer, - *const hoedown_renderer_data); + *const hoedown_renderer_data, libc::size_t); type codespanfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer, - *const hoedown_renderer_data) -> libc::c_int; + *const hoedown_renderer_data, libc::size_t) -> libc::c_int; type linkfn = extern "C" fn (*mut hoedown_buffer, *const hoedown_buffer, *const hoedown_buffer, *const hoedown_buffer, - *const hoedown_renderer_data) -> libc::c_int; + *const hoedown_renderer_data, libc::size_t) -> libc::c_int; type entityfn = extern "C" fn (*mut hoedown_buffer, *const hoedown_buffer, - *const hoedown_renderer_data); + *const hoedown_renderer_data, libc::size_t); type normaltextfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer, - *const hoedown_renderer_data); + *const hoedown_renderer_data, libc::size_t); #[repr(C)] struct hoedown_renderer_data { @@ -147,7 +149,8 @@ struct html_toc_data { struct MyOpaque { dfltblk: extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer, - *const hoedown_buffer, *const hoedown_renderer_data), + *const hoedown_buffer, *const hoedown_renderer_data, + libc::size_t), toc_builder: Option, } @@ -229,7 +232,8 @@ pub fn render(w: &mut fmt::Formatter, print_toc: bool, html_flags: libc::c_uint) -> fmt::Result { extern fn block(ob: *mut hoedown_buffer, orig_text: *const hoedown_buffer, - lang: *const hoedown_buffer, data: *const hoedown_renderer_data) { + lang: *const hoedown_buffer, data: *const hoedown_renderer_data, + line: libc::size_t) { unsafe { if orig_text.is_null() { return } @@ -246,7 +250,8 @@ pub fn render(w: &mut fmt::Formatter, let rlang = str::from_utf8(rlang).unwrap(); if !LangString::parse(rlang).rust { (my_opaque.dfltblk)(ob, orig_text, lang, - opaque as *const hoedown_renderer_data); + opaque as *const hoedown_renderer_data, + line); true } else { false @@ -312,7 +317,8 @@ pub fn render(w: &mut fmt::Formatter, } extern fn header(ob: *mut hoedown_buffer, text: *const hoedown_buffer, - level: libc::c_int, data: *const hoedown_renderer_data) { + level: libc::c_int, data: *const hoedown_renderer_data, + _: libc::size_t) { // hoedown does this, we may as well too unsafe { hoedown_buffer_puts(ob, "\n\0".as_ptr() as *const _); } @@ -373,6 +379,7 @@ pub fn render(w: &mut fmt::Formatter, ob: *mut hoedown_buffer, text: *const hoedown_buffer, _: *const hoedown_renderer_data, + _: libc::size_t ) -> libc::c_int { let content = if text.is_null() { "".to_owned() @@ -422,11 +429,12 @@ pub fn render(w: &mut fmt::Formatter, } } -pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) { +pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector, start_line: usize) { extern fn block(_ob: *mut hoedown_buffer, text: *const hoedown_buffer, lang: *const hoedown_buffer, - data: *const hoedown_renderer_data) { + data: *const hoedown_renderer_data, + line: libc::size_t) { unsafe { if text.is_null() { return } let block_info = if lang.is_null() { @@ -445,16 +453,19 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) { stripped_filtered_line(l).unwrap_or(l) }); let text = lines.collect::>().join("\n"); + let line = tests.get_line() + line; tests.add_test(text.to_owned(), block_info.should_panic, block_info.no_run, block_info.ignore, block_info.test_harness, - block_info.compile_fail, block_info.error_codes); + block_info.compile_fail, block_info.error_codes, + line); } } extern fn header(_ob: *mut hoedown_buffer, text: *const hoedown_buffer, - level: libc::c_int, data: *const hoedown_renderer_data) { + level: libc::c_int, data: *const hoedown_renderer_data, + _: libc::size_t) { unsafe { let opaque = (*data).opaque as *mut hoedown_html_renderer_state; let tests = &mut *((*opaque).opaque as *mut ::test::Collector); @@ -468,6 +479,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) { } } + tests.set_line(start_line); unsafe { let ob = hoedown_buffer_new(DEF_OUNIT); let renderer = hoedown_html_renderer_new(0, 0); @@ -488,6 +500,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) { #[derive(Eq, PartialEq, Clone, Debug)] struct LangString { + original: String, should_panic: bool, no_run: bool, ignore: bool, @@ -500,6 +513,7 @@ struct LangString { impl LangString { fn all_false() -> LangString { LangString { + original: String::new(), should_panic: false, no_run: false, ignore: false, @@ -521,6 +535,7 @@ impl LangString { allow_error_code_check = true; } + data.original = string.to_owned(); let tokens = string.split(|c: char| !(c == '_' || c == '-' || c.is_alphanumeric()) ); @@ -586,7 +601,8 @@ pub fn plain_summary_line(md: &str) -> String { _link: *const hoedown_buffer, _title: *const hoedown_buffer, content: *const hoedown_buffer, - data: *const hoedown_renderer_data) -> libc::c_int + data: *const hoedown_renderer_data, + _: libc::size_t) -> libc::c_int { unsafe { if !content.is_null() && (*content).size > 0 { @@ -599,8 +615,9 @@ pub fn plain_summary_line(md: &str) -> String { } extern fn normal_text(_ob: *mut hoedown_buffer, - text: *const hoedown_buffer, - data: *const hoedown_renderer_data) + text: *const hoedown_buffer, + data: *const hoedown_renderer_data, + _: libc::size_t) { unsafe { let ob = (*data).opaque as *mut hoedown_buffer; @@ -647,6 +664,7 @@ mod tests { test_harness: test_harness, compile_fail: compile_fail, error_codes: error_codes, + original: s.to_owned(), }) } diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 369e18948ad..49497957be9 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -154,8 +154,9 @@ pub fn test(input: &str, cfgs: Vec, libs: SearchPaths, externs: Externs, let mut opts = TestOptions::default(); opts.no_crate_inject = true; let mut collector = Collector::new(input.to_string(), cfgs, libs, externs, - true, opts, maybe_sysroot); - find_testable_code(&input_str, &mut collector); + true, opts, maybe_sysroot, "input".to_string(), + None); + find_testable_code(&input_str, &mut collector, 0); test_args.insert(0, "rustdoctest".to_string()); testing::test_main(&test_args, collector.tests); 0 diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index ab0ac02fd88..6b6330ef12a 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -37,6 +37,7 @@ use rustc_trans::back::link; use syntax::ast; use syntax::codemap::CodeMap; use syntax::feature_gate::UnstableFeatures; +use syntax_pos::{BytePos, DUMMY_SP, Pos}; use errors; use errors::emitter::ColorConfig; @@ -79,7 +80,7 @@ pub fn run(input: &str, let _ignore = dep_graph.in_ignore(); let cstore = Rc::new(CStore::new(&dep_graph)); let mut sess = session::build_session_( - sessopts, &dep_graph, Some(input_path.clone()), handler, codemap, cstore.clone(), + sessopts, &dep_graph, Some(input_path.clone()), handler, codemap.clone(), cstore.clone(), ); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); sess.parse_sess.config = @@ -96,13 +97,16 @@ pub fn run(input: &str, link::find_crate_name(None, &hir_forest.krate().attrs, &input) }); let opts = scrape_test_config(hir_forest.krate()); + let filename = input_path.to_str().unwrap_or("").to_owned(); let mut collector = Collector::new(crate_name, cfgs, libs, externs, false, opts, - maybe_sysroot); + maybe_sysroot, + filename, + Some(codemap)); { let dep_graph = DepGraph::new(false); @@ -256,7 +260,9 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec, libs: SearchPaths, error_codes.retain(|err| !out.contains(err)); } } - Ok(()) if compile_fail => panic!("test compiled while it wasn't supposed to"), + Ok(()) if compile_fail => { + panic!("test compiled while it wasn't supposed to") + } _ => {} } } @@ -302,7 +308,7 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec, libs: SearchPaths, if should_panic && out.status.success() { panic!("test executable succeeded when it should have failed"); } else if !should_panic && !out.status.success() { - panic!("test executable failed:\n{}\n{}", + panic!("test executable failed:\n{}\n{}\n", str::from_utf8(&out.stdout).unwrap_or(""), str::from_utf8(&out.stderr).unwrap_or("")); } @@ -384,11 +390,15 @@ pub struct Collector { cratename: String, opts: TestOptions, maybe_sysroot: Option, + filename: String, + start_line: usize, + codemap: Option>, } impl Collector { pub fn new(cratename: String, cfgs: Vec, libs: SearchPaths, externs: Externs, - use_headers: bool, opts: TestOptions, maybe_sysroot: Option) -> Collector { + use_headers: bool, opts: TestOptions, maybe_sysroot: Option, + filename: String, codemap: Option>) -> Collector { Collector { tests: Vec::new(), names: Vec::new(), @@ -401,18 +411,17 @@ impl Collector { cratename: cratename, opts: opts, maybe_sysroot: maybe_sysroot, + filename: filename, + start_line: 0, + codemap: codemap, } } pub fn add_test(&mut self, test: String, should_panic: bool, no_run: bool, should_ignore: bool, - as_test_harness: bool, compile_fail: bool, error_codes: Vec) { - let name = if self.use_headers { - let s = self.current_header.as_ref().map(|s| &**s).unwrap_or(""); - format!("{}_{}", s, self.cnt) - } else { - format!("{}_{}", self.names.join("::"), self.cnt) - }; + as_test_harness: bool, compile_fail: bool, error_codes: Vec, + line: usize) { + let name = format!("{} - line {}", self.filename, line); self.cnt += 1; let cfgs = self.cfgs.clone(); let libs = self.libs.clone(); @@ -456,6 +465,19 @@ impl Collector { }); } + pub fn get_line(&self) -> usize { + if let Some(ref codemap) = self.codemap{ + let line = codemap.lookup_char_pos(BytePos(self.start_line as u32)).line; + if line > 0 { line - 1 } else { line } + } else { + self.start_line + } + } + + pub fn set_line(&mut self, start_line: usize) { + self.start_line = start_line; + } + pub fn register_header(&mut self, name: &str, level: u32) { if self.use_headers && level == 1 { // we use these headings as test names, so it's good if @@ -496,7 +518,8 @@ impl<'a, 'hir> HirCollector<'a, 'hir> { attrs.unindent_doc_comments(); if let Some(doc) = attrs.doc_value() { self.collector.cnt = 0; - markdown::find_testable_code(doc, self.collector); + markdown::find_testable_code(doc, self.collector, + attrs.span.unwrap_or(DUMMY_SP).lo.to_usize()); } nested(self); diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 099ca8f02d2..455a6a0fb32 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -280,9 +280,9 @@ impl Attribute { Symbol::intern("doc"), Symbol::intern(&strip_doc_comment_decoration(&comment.as_str()))); if self.style == ast::AttrStyle::Outer { - f(&mk_attr_outer(self.id, meta)) + f(&mk_attr_outer(self.span, self.id, meta)) } else { - f(&mk_attr_inner(self.id, meta)) + f(&mk_attr_inner(self.span, self.id, meta)) } } else { f(self) @@ -339,8 +339,8 @@ pub fn mk_attr_id() -> AttrId { } /// Returns an inner attribute with the given value. -pub fn mk_attr_inner(id: AttrId, item: MetaItem) -> Attribute { - mk_spanned_attr_inner(DUMMY_SP, id, item) +pub fn mk_attr_inner(span: Span, id: AttrId, item: MetaItem) -> Attribute { + mk_spanned_attr_inner(span, id, item) } /// Returns an innter attribute with the given value and span. @@ -356,8 +356,8 @@ pub fn mk_spanned_attr_inner(sp: Span, id: AttrId, item: MetaItem) -> Attribute /// Returns an outer attribute with the given value. -pub fn mk_attr_outer(id: AttrId, item: MetaItem) -> Attribute { - mk_spanned_attr_outer(DUMMY_SP, id, item) +pub fn mk_attr_outer(span: Span, id: AttrId, item: MetaItem) -> Attribute { + mk_spanned_attr_outer(span, id, item) } /// Returns an outer attribute with the given value and span. diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ffdd3b2e93e..f8f1820d0b9 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -27,6 +27,7 @@ use print::pp::Breaks::{Consistent, Inconsistent}; use ptr::P; use std_inject; use symbol::{Symbol, keywords}; +use syntax_pos::DUMMY_SP; use tokenstream::{self, TokenTree}; use std::ascii; @@ -116,12 +117,12 @@ pub fn print_crate<'a>(cm: &'a CodeMap, // #![feature(prelude_import)] let prelude_import_meta = attr::mk_list_word_item(Symbol::intern("prelude_import")); let list = attr::mk_list_item(Symbol::intern("feature"), vec![prelude_import_meta]); - let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), list); + let fake_attr = attr::mk_attr_inner(DUMMY_SP, attr::mk_attr_id(), list); s.print_attribute(&fake_attr)?; // #![no_std] let no_std_meta = attr::mk_word_item(Symbol::intern("no_std")); - let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), no_std_meta); + let fake_attr = attr::mk_attr_inner(DUMMY_SP, attr::mk_attr_id(), no_std_meta); s.print_attribute(&fake_attr)?; } diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 68d807b24a7..4a2dfaf6124 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -56,7 +56,8 @@ pub fn maybe_inject_crates_ref(sess: &ParseSess, let crate_name = Symbol::intern(&alt_std_name.unwrap_or(name.to_string())); krate.module.items.insert(0, P(ast::Item { - attrs: vec![attr::mk_attr_outer(attr::mk_attr_id(), + attrs: vec![attr::mk_attr_outer(DUMMY_SP, + attr::mk_attr_id(), attr::mk_word_item(Symbol::intern("macro_use")))], vis: ast::Visibility::Inherited, node: ast::ItemKind::ExternCrate(Some(crate_name)), diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 74ec33fdd2a..dd2756cd2b2 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -195,7 +195,8 @@ impl fold::Folder for EntryPointCleaner { let dead_code_str = Symbol::intern("dead_code"); let word_vec = vec![attr::mk_list_word_item(dead_code_str)]; let allow_dead_code_item = attr::mk_list_item(allow_str, word_vec); - let allow_dead_code = attr::mk_attr_outer(attr::mk_attr_id(), + let allow_dead_code = attr::mk_attr_outer(DUMMY_SP, + attr::mk_attr_id(), allow_dead_code_item); ast::Item { diff --git a/src/rt/hoedown b/src/rt/hoedown index a3736a0a190..da282f1bb72 160000 --- a/src/rt/hoedown +++ b/src/rt/hoedown @@ -1 +1 @@ -Subproject commit a3736a0a1907cbc8bf619708738815a5fd789c80 +Subproject commit da282f1bb7277b4d30fa1599ee29ad8eb4dd2a92 diff --git a/src/test/run-make/issue-22131/Makefile b/src/test/run-make/issue-22131/Makefile index 1e8568626a6..f65cc9e06a3 100644 --- a/src/test/run-make/issue-22131/Makefile +++ b/src/test/run-make/issue-22131/Makefile @@ -4,4 +4,4 @@ all: foo.rs $(RUSTC) --cfg 'feature="bar"' --crate-type lib foo.rs $(HOST_RPATH_ENV) '$(RUSTDOC)' --test --cfg 'feature="bar"' \ -L $(TMPDIR) foo.rs |\ - grep -q 'test foo_0 ... ok' + grep -q 'foo.rs - line 11 ... ok' diff --git a/src/test/rustdoc/test_option_check/test.rs b/src/test/rustdoc/test_option_check/test.rs new file mode 100644 index 00000000000..b2afe43204d --- /dev/null +++ b/src/test/rustdoc/test_option_check/test.rs @@ -0,0 +1,26 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: --test +// check-test-line-numbers-match + +/// This is a Foo; +/// +/// ``` +/// println!("baaaaaar"); +/// ``` +pub struct Foo; + +/// This is a Bar; +/// +/// ``` +/// println!("fooooo"); +/// ``` +pub struct Bar; diff --git a/src/tools/cargotest/main.rs b/src/tools/cargotest/main.rs index 83fd766c547..786b3192e06 100644 --- a/src/tools/cargotest/main.rs +++ b/src/tools/cargotest/main.rs @@ -25,7 +25,7 @@ const TEST_REPOS: &'static [Test] = &[ Test { name: "cargo", repo: "https://github.com/rust-lang/cargo", - sha: "2324c2bbaf7fc6ea9cbdd77c034ef1af769cb617", + sha: "0e1e34be7540bdaed4918457654fbf028cf69e56", lock: None, }, Test { diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index ac1ac1c2f6c..71d8d62c75b 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -224,6 +224,8 @@ pub struct TestProps { pub incremental_dir: Option, // Specifies that a cfail test must actually compile without errors. pub must_compile_successfully: bool, + // rustdoc will test the output of the `--test` option + pub check_test_line_numbers_match: bool, } impl TestProps { @@ -248,6 +250,7 @@ impl TestProps { forbid_output: vec![], incremental_dir: None, must_compile_successfully: false, + check_test_line_numbers_match: false, } } @@ -347,6 +350,10 @@ impl TestProps { if !self.must_compile_successfully { self.must_compile_successfully = parse_must_compile_successfully(ln); } + + if !self.check_test_line_numbers_match { + self.check_test_line_numbers_match = parse_check_test_line_numbers_match(ln); + } }); for key in vec!["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] { @@ -458,6 +465,10 @@ fn parse_must_compile_successfully(line: &str) -> bool { parse_name_directive(line, "must-compile-successfully") } +fn parse_check_test_line_numbers_match(line: &str) -> bool { + parse_name_directive(line, "check-test-line-numbers-match") +} + fn parse_env(line: &str, name: &str) -> Option<(String, String)> { parse_name_value_directive(line, name).map(|nv| { // nv is either FOO or FOO=BAR diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 86fa5e70c9c..a8c46722e16 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -43,7 +43,7 @@ pub fn run(config: Config, testpaths: &TestPaths) { } _ => { - // android has it's own gdb handling + // android has its own gdb handling if config.mode == DebugInfoGdb && config.gdb.is_none() { panic!("gdb not available but debuginfo gdb debuginfo test requested"); } @@ -1887,14 +1887,75 @@ actual:\n\ if !proc_res.status.success() { self.fatal_proc_rec("rustdoc failed!", &proc_res); } - let root = self.find_rust_src_root().unwrap(); - let res = self.cmd2procres(Command::new(&self.config.docck_python) - .arg(root.join("src/etc/htmldocck.py")) - .arg(out_dir) - .arg(&self.testpaths.file)); - if !res.status.success() { - self.fatal_proc_rec("htmldocck failed!", &res); + if self.props.check_test_line_numbers_match == true { + self.check_rustdoc_test_option(proc_res); + } else { + let root = self.find_rust_src_root().unwrap(); + let res = self.cmd2procres(Command::new(&self.config.docck_python) + .arg(root.join("src/etc/htmldocck.py")) + .arg(out_dir) + .arg(&self.testpaths.file)); + if !res.status.success() { + self.fatal_proc_rec("htmldocck failed!", &res); + } + } + } + + fn check_rustdoc_test_option(&self, res: ProcRes) { + let mut file = fs::File::open(&self.testpaths.file) + .expect("markdown_test_output_check_entry File::open failed"); + let mut content = String::new(); + file.read_to_string(&mut content) + .expect("markdown_test_output_check_entry read_to_string failed"); + let mut ignore = false; + let mut v: Vec = + content.lines() + .enumerate() + .filter_map(|(line_nb, line)| { + let sline = line.split("///").last().unwrap_or(""); + let line = sline.trim_left(); + if line.starts_with("```") { + if ignore { + ignore = false; + None + } else { + ignore = true; + Some(line_nb + 1) + } + } else { + None + } + }) + .collect(); + + let mut tested = 0; + for _ in res.stdout.split("\n") + .filter(|s| s.starts_with("test ")) + .inspect(|s| { + let tmp: Vec<&str> = s.split(" - line ").collect(); + if tmp.len() == 2 { + tested += 1; + let line = tmp[1].split(" ...") + .next() + .unwrap_or("0") + .parse() + .unwrap_or(0); + if let Ok(pos) = v.binary_search(&line) { + v.remove(pos); + } else { + self.fatal_proc_rec( + &format!("Not found doc test: \"{}\" in {:?}", s, v), + &res); + } + } + }) {} + if tested == 0 { + self.fatal_proc_rec("No test has been found", &res); + } else if v.len() != 0 { + self.fatal_proc_rec(&format!("Not found test at line{} {:?}", + if v.len() > 1 { "s" } else { "" }, v), + &res); } }