use rustc_span::edition::{DEFAULT_EDITION, Edition}; use super::{ ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, LangStringToken, Markdown, MarkdownItemInfo, TagIterator, find_testable_code, plain_text_summary, short_markdown_summary, }; #[test] fn test_unique_id() { let input = [ "foo", "examples", "examples", "method.into_iter", "examples", "method.into_iter", "foo", "main-content", "search", "methods", "examples", "method.into_iter", "assoc_type.Item", "assoc_type.Item", ]; let expected = [ "foo", "examples", "examples-1", "method.into_iter", "examples-2", "method.into_iter-1", "foo-1", "main-content-1", "search-1", "methods", "examples-3", "method.into_iter-2", "assoc_type.Item", "assoc_type.Item-1", ]; let mut map = IdMap::new(); let actual: Vec = input.iter().map(|s| map.derive(s)).collect(); assert_eq!(&actual[..], expected); } #[test] fn test_lang_string_parse() { fn t(lg: LangString) { let s = &lg.original; assert_eq!(LangString::parse(s, ErrorCodes::Yes, None), lg) } t(Default::default()); t(LangString { original: "rust".into(), ..Default::default() }); t(LangString { original: "rusta".into(), rust: false, unknown: vec!["rusta".into()], ..Default::default() }); // error t(LangString { original: "{rust}".into(), rust: false, ..Default::default() }); t(LangString { original: "{.rust}".into(), rust: true, added_classes: vec!["rust".into()], ..Default::default() }); t(LangString { original: "custom,{.rust}".into(), rust: false, added_classes: vec!["rust".into()], ..Default::default() }); t(LangString { original: "sh".into(), rust: false, unknown: vec!["sh".into()], ..Default::default() }); t(LangString { original: "ignore".into(), ignore: Ignore::All, ..Default::default() }); t(LangString { original: "ignore-foo".into(), ignore: Ignore::Some(vec!["foo".to_string()]), ..Default::default() }); t(LangString { original: "should_panic".into(), should_panic: true, ..Default::default() }); t(LangString { original: "no_run".into(), no_run: true, ..Default::default() }); t(LangString { original: "test_harness".into(), test_harness: true, ..Default::default() }); t(LangString { original: "compile_fail".into(), no_run: true, compile_fail: true, ..Default::default() }); t(LangString { original: "no_run,example".into(), no_run: true, unknown: vec!["example".into()], ..Default::default() }); t(LangString { original: "sh,should_panic".into(), should_panic: true, rust: false, unknown: vec!["sh".into()], ..Default::default() }); t(LangString { original: "example,rust".into(), unknown: vec!["example".into()], ..Default::default() }); t(LangString { original: "test_harness,rusta".into(), test_harness: true, unknown: vec!["rusta".into()], ..Default::default() }); t(LangString { original: "text, no_run".into(), no_run: true, rust: false, unknown: vec!["text".into()], ..Default::default() }); t(LangString { original: "text,no_run".into(), no_run: true, rust: false, unknown: vec!["text".into()], ..Default::default() }); t(LangString { original: "text,no_run, ".into(), no_run: true, rust: false, unknown: vec!["text".into()], ..Default::default() }); t(LangString { original: "text,no_run,".into(), no_run: true, rust: false, unknown: vec!["text".into()], ..Default::default() }); t(LangString { original: "edition2015".into(), edition: Some(Edition::Edition2015), ..Default::default() }); t(LangString { original: "edition2018".into(), edition: Some(Edition::Edition2018), ..Default::default() }); t(LangString { original: "{class=test}".into(), added_classes: vec!["test".into()], rust: true, ..Default::default() }); t(LangString { original: "custom,{class=test}".into(), added_classes: vec!["test".into()], rust: false, ..Default::default() }); t(LangString { original: "{.test}".into(), added_classes: vec!["test".into()], rust: true, ..Default::default() }); t(LangString { original: "custom,{.test}".into(), added_classes: vec!["test".into()], rust: false, ..Default::default() }); t(LangString { original: "rust,{class=test,.test2}".into(), added_classes: vec!["test".into(), "test2".into()], rust: true, ..Default::default() }); t(LangString { original: "{class=test:with:colon .test1}".into(), added_classes: vec!["test:with:colon".into(), "test1".into()], rust: true, ..Default::default() }); t(LangString { original: "custom,{class=test:with:colon .test1}".into(), added_classes: vec!["test:with:colon".into(), "test1".into()], rust: false, ..Default::default() }); t(LangString { original: "{class=first,class=second}".into(), added_classes: vec!["first".into(), "second".into()], rust: true, ..Default::default() }); t(LangString { original: "custom,{class=first,class=second}".into(), added_classes: vec!["first".into(), "second".into()], rust: false, ..Default::default() }); t(LangString { original: "{class=first,.second},unknown".into(), added_classes: vec!["first".into(), "second".into()], rust: false, unknown: vec!["unknown".into()], ..Default::default() }); t(LangString { original: "{class=first .second} unknown".into(), added_classes: vec!["first".into(), "second".into()], rust: false, unknown: vec!["unknown".into()], ..Default::default() }); // error t(LangString { original: "{.first.second}".into(), rust: true, added_classes: vec!["first.second".into()], ..Default::default() }); // error t(LangString { original: "{class=first=second}".into(), rust: false, ..Default::default() }); // error t(LangString { original: "{class=first.second}".into(), rust: true, added_classes: vec!["first.second".into()], ..Default::default() }); // error t(LangString { original: "{class=.first}".into(), added_classes: vec![".first".into()], rust: true, ..Default::default() }); t(LangString { original: r#"{class="first"}"#.into(), added_classes: vec!["first".into()], rust: true, ..Default::default() }); t(LangString { original: r#"custom,{class="first"}"#.into(), added_classes: vec!["first".into()], rust: false, ..Default::default() }); // error t(LangString { original: r#"{class=f"irst"}"#.into(), rust: false, ..Default::default() }); } #[test] fn test_lang_string_tokenizer() { fn case(lang_string: &str, want: &[LangStringToken<'_>]) { let have = TagIterator::new(lang_string, None).collect::>(); assert_eq!(have, want, "Unexpected lang string split for `{}`", lang_string); } case("", &[]); case("foo", &[LangStringToken::LangToken("foo")]); case("foo,bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]); case(".foo,.bar", &[]); case( "{.foo,.bar}", &[LangStringToken::ClassAttribute("foo"), LangStringToken::ClassAttribute("bar")], ); case( " {.foo,.bar} ", &[LangStringToken::ClassAttribute("foo"), LangStringToken::ClassAttribute("bar")], ); case("foo bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]); case("foo\tbar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]); case("foo\t, bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]); case(" foo , bar ", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]); case(",,foo,,bar,,", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]); case("foo=bar", &[]); case("a-b-c", &[LangStringToken::LangToken("a-b-c")]); case("a_b_c", &[LangStringToken::LangToken("a_b_c")]); } #[test] fn test_header() { fn t(input: &str, expect: &str) { let mut map = IdMap::new(); let mut output = String::new(); Markdown { content: input, links: &[], ids: &mut map, error_codes: ErrorCodes::Yes, edition: DEFAULT_EDITION, playground: &None, heading_offset: HeadingOffset::H2, } .write_into(&mut output) .unwrap(); assert_eq!(output, expect, "original: {}", input); } t( "# Foo bar", "

§Foo bar

", ); t( "## Foo-bar_baz qux", "

\ §\ Foo-bar_baz qux\

", ); t( "### **Foo** *bar* baz!?!& -_qux_-%", "

\ §\ Foo bar baz!?!& -qux-%\

", ); t( "#### **Foo?** & \\*bar?!* _`baz`_ ❤ #qux", "
\ §\ Foo? & *bar?!* baz ❤ #qux\
", ); t( "# Foo [bar](https://hello.yo)", "

\ §\ Foo bar\

", ); } #[test] fn test_header_ids_multiple_blocks() { let mut map = IdMap::new(); fn t(map: &mut IdMap, input: &str, expect: &str) { let mut output = String::new(); Markdown { content: input, links: &[], ids: map, error_codes: ErrorCodes::Yes, edition: DEFAULT_EDITION, playground: &None, heading_offset: HeadingOffset::H2, } .write_into(&mut output) .unwrap(); assert_eq!(output, expect, "original: {}", input); } t( &mut map, "# Example", "

§Example

", ); t( &mut map, "# Panics", "

§Panics

", ); t( &mut map, "# Example", "

§Example

", ); t( &mut map, "# Search", "

§Search

", ); t( &mut map, "# Example", "

§Example

", ); t( &mut map, "# Panics", "

§Panics

", ); } #[test] fn test_short_markdown_summary() { fn t(input: &str, expect: &str) { let output = short_markdown_summary(input, &[][..]); assert_eq!(output, expect, "original: {}", input); } t("", ""); t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)"); t("*italic*", "italic"); t("**bold**", "bold"); t("Multi-line\nsummary", "Multi-line summary"); t("Hard-break \nsummary", "Hard-break summary"); t("hello [Rust] :)\n\n[Rust]: https://www.rust-lang.org", "hello Rust :)"); t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)"); t("dud [link]", "dud [link]"); t("code `let x = i32;` ...", "code let x = i32; …"); t("type `Type<'static>` ...", "type Type<'static> …"); // Test to ensure escaping and length-limiting work well together. // The output should be limited based on the input length, // rather than the output, because escaped versions of characters // are usually longer than how the character is actually displayed. t( "& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & &", "& & & & & & & & & & & & \ & & & & & & & & & & & & \ & & & & & …", ); t("# top header", "top header"); t("# top header\n\nfollowed by a paragraph", "top header"); t("## header", "header"); t("first paragraph\n\nsecond paragraph", "first paragraph"); t("```\nfn main() {}\n```", ""); t("
hello
", ""); t( "a *very*, **very** long first paragraph. it has lots of `inline code: Vec`. and it has a [link](https://www.rust-lang.org).\nthat was a soft line break! \nthat was a hard one\n\nsecond paragraph.", "a very, very long first paragraph. it has lots of …", ); } #[test] fn test_plain_text_summary() { fn t(input: &str, expect: &str) { let output = plain_text_summary(input, &[]); assert_eq!(output, expect, "original: {}", input); } t("", ""); t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)"); t("**bold**", "bold"); t("Multi-line\nsummary", "Multi-line summary"); t("Hard-break \nsummary", "Hard-break summary"); t("hello [Rust] :)\n\n[Rust]: https://www.rust-lang.org", "hello Rust :)"); t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)"); t("dud [link]", "dud [link]"); t("code `let x = i32;` ...", "code `let x = i32;` …"); t("type `Type<'static>` ...", "type `Type<'static>` …"); t("# top header", "top header"); t("# top header\n\nfollowed by some text", "top header"); t("## header", "header"); t("first paragraph\n\nsecond paragraph", "first paragraph"); t("```\nfn main() {}\n```", ""); t("
hello
", ""); t( "a *very*, **very** long first paragraph. it has lots of `inline code: Vec`. and it has a [link](https://www.rust-lang.org).\nthat was a soft line break! \nthat was a hard one\n\nsecond paragraph.", "a very, very long first paragraph. it has lots of `inline code: Vec`. and it has a link. that was a soft line break! that was a hard one", ); } #[test] fn test_markdown_html_escape() { fn t(input: &str, expect: &str) { let mut idmap = IdMap::new(); let mut output = String::new(); MarkdownItemInfo(input, &mut idmap).write_into(&mut output).unwrap(); assert_eq!(output, expect, "original: {}", input); } t("`Struct<'a, T>`", "Struct<'a, T>"); t("Struct<'a, T>", "Struct<’a, T>"); t("Struct
", "Struct<br>"); } #[test] fn test_find_testable_code_line() { fn t(input: &str, expect: &[usize]) { let mut lines = Vec::::new(); find_testable_code(input, &mut lines, ErrorCodes::No, None); assert_eq!(lines, expect); } t("", &[]); t("```rust\n```", &[1]); t(" ```rust\n```", &[1]); t("\n```rust\n```", &[2]); t("\n ```rust\n```", &[2]); t("```rust\n```\n```rust\n```", &[1, 3]); t("```rust\n```\n ```rust\n```", &[1, 3]); } #[test] fn test_ascii_with_prepending_hashtag() { fn t(input: &str, expect: &str) { let mut map = IdMap::new(); let mut output = String::new(); Markdown { content: input, links: &[], ids: &mut map, error_codes: ErrorCodes::Yes, edition: DEFAULT_EDITION, playground: &None, heading_offset: HeadingOffset::H2, } .write_into(&mut output) .unwrap(); assert_eq!(output, expect, "original: {}", input); } t( r#"```ascii #..#.####.#....#.....##.. #..#.#....#....#....#..#. ####.###..#....#....#..#. #..#.#....#....#....#..#. #..#.#....#....#....#..#. #..#.####.####.####..##.. ```"#, "
\
#..#.####.#....#.....##..
#..#.#....#....#....#..#.
####.###..#....#....#..#.
#..#.#....#....#....#..#.
#..#.#....#....#....#..#.
#..#.####.####.####..##..
", ); t( r#"```markdown # hello ```"#, "
\
# hello
", ); }