package com.example.aiapp import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue /** * What the scanner colours, asserted as the text under each span rather than as offsets, so a * failure prints the code that was got wrong instead of a pair of numbers. * * Most of these are the mistakes dev.snipme:highlights 1.1.0 made -- the library this scanner * replaced -- measured against it directly before it was removed. They are here rather than in the * `highlights-repro.sh` script they came from because a case that only a script can ask about is a * case nobody asks about. */ class HighlighterTest { /** Every span of [kind] in [code], as the text it covers. */ private fun spans(code: String, language: Language, kind: Kind): List = spansOf(code, language).filter { it.kind == kind }.map { code.substring(it.start, it.end) } private fun assertSpans( code: String, language: Language, kind: Kind, vararg expected: String, ) { assertEquals(expected.toList(), spans(code, language, kind), "$kind in: $code") } // The four inputs the repro script asked the library about, and what it answered. @Test fun `a quoted glob is one string, not a comment`() { // The library answered a span whose end preceded its start here, which crashed the app. assertSpans("x '*/a/*'", Language.SHELL, Kind.STRING, "'*/a/*'") assertSpans("x '*/a/*'", Language.SHELL, Kind.COMMENT) } @Test fun `a find with globs has no comment in it`() { val code = "find . -path '*/.git/*' -prune -o -name '*.kt' -print" assertSpans(code, Language.SHELL, Kind.STRING, "'*/.git/*'", "'*.kt'") assertSpans(code, Language.SHELL, Kind.COMMENT) } @Test fun `a URL does not comment out the rest of a shell line`() { val code = "curl https://example.com/x && echo done" assertSpans(code, Language.SHELL, Kind.COMMENT) assertSpans(code, Language.SHELL, Kind.KEYWORD, "echo") } @Test fun `a URL inside a Kotlin string stays a string`() { val code = "val url = \"https://example.com\"\nfun f() = 1" assertSpans(code, Language.KOTLIN, Kind.COMMENT) assertSpans(code, Language.KOTLIN, Kind.STRING, "\"https://example.com\"") assertSpans(code, Language.KOTLIN, Kind.KEYWORD, "val", "fun") } // Attributes, which the library greyed out as comments. @Test fun `a Rust attribute is metadata and the struct after it still colours`() { val code = "#[derive(Debug)]\nstruct A { b: u8 }" assertSpans(code, Language.RUST, Kind.METADATA, "#[derive(Debug)]") assertSpans(code, Language.RUST, Kind.COMMENT) assertSpans(code, Language.RUST, Kind.KEYWORD, "struct") } @Test fun `an inner Rust attribute closes at its own bracket`() { val code = "#![allow(dead_code)]\nfn f() {}" assertSpans(code, Language.RUST, Kind.METADATA, "#![allow(dead_code)]") assertSpans(code, Language.RUST, Kind.KEYWORD, "fn") } @Test fun `a C preprocessor line is metadata rather than a comment`() { val code = "#include \nint main() { return 0; }" assertSpans(code, Language.C, Kind.METADATA, "#include ") assertSpans(code, Language.C, Kind.COMMENT) assertSpans(code, Language.C, Kind.KEYWORD, "int", "return") } @Test fun `a Kotlin annotation is metadata`() { assertSpans("@Composable fun f() {}", Language.KOTLIN, Kind.METADATA, "@Composable") } // Strings whose contents the library read as code. @Test fun `a hash inside a Kotlin string is not a comment`() { val code = "val c = \"#FF0000\"\nval d = 1" assertSpans(code, Language.KOTLIN, Kind.COMMENT) assertSpans(code, Language.KOTLIN, Kind.STRING, "\"#FF0000\"") } @Test fun `an apostrophe inside a Kotlin string does not open one`() { val code = "val a = \"don't\"\nval b = \"x\"" assertSpans(code, Language.KOTLIN, Kind.STRING, "\"don't\"", "\"x\"") } @Test fun `a Rust lifetime does not open a string but a character literal does`() { val code = "fn f<'a>(x: &'a str) { let c = 'x'; }" assertSpans(code, Language.RUST, Kind.STRING, "'x'") } @Test fun `an escaped quote is inside the Rust character literal`() { assertSpans("let c = '\\'';", Language.RUST, Kind.STRING, "'\\''") } @Test fun `a Rust raw string keeps its inner quotes`() { val code = "let s = r#\"a \"quoted\" b\"#;" assertSpans(code, Language.RUST, Kind.STRING, "r#\"a \"quoted\" b\"#") } @Test fun `a Kotlin triple quoted string is one string`() { assertSpans( "val s = \"\"\"a \"b\" c\"\"\"", Language.KOTLIN, Kind.STRING, "\"\"\"a \"b\" c\"\"\"", ) } @Test fun `a shell single quoted string takes no escapes`() { // `\` is literal inside shell single quotes, so the string ends at the next apostrophe. assertSpans("echo 'a\\' b", Language.SHELL, Kind.STRING, "'a\\'") } // Comments. @Test fun `Rust and Kotlin nest block comments`() { val code = "/* a /* b */ c */ x" assertSpans(code, Language.RUST, Kind.COMMENT, "/* a /* b */ c */") assertSpans(code, Language.KOTLIN, Kind.COMMENT, "/* a /* b */ c */") } @Test fun `C ends a block comment at the first close`() { assertSpans("/* a /* b */ c */ x", Language.C, Kind.COMMENT, "/* a /* b */") } @Test fun `a shell comment starts only at a word boundary`() { val code = "\${#x} \$# a#b # real" assertSpans(code, Language.SHELL, Kind.COMMENT, "# real") } @Test fun `a hash anywhere is a Python comment`() { assertSpans("x = 1 # note", Language.PYTHON, Kind.COMMENT, "# note") } // TOML, which the library has no rules for at all. @Test fun `a TOML table header is metadata and a hash in a value is not a comment`() { val code = "[server]\ncolour = \"#FF0000\"\nport = 8080 # the real one" assertSpans(code, Language.TOML, Kind.METADATA, "[server]") assertSpans(code, Language.TOML, Kind.STRING, "\"#FF0000\"") assertSpans(code, Language.TOML, Kind.COMMENT, "# the real one") assertSpans(code, Language.TOML, Kind.LITERAL, "8080") } @Test fun `a RON attribute and its values colour`() { val code = "#![enable(implicit_some)]\n(count: 3, on: true)" assertSpans(code, Language.RON, Kind.METADATA, "#![enable(implicit_some)]") assertSpans(code, Language.RON, Kind.KEYWORD, "true") assertSpans(code, Language.RON, Kind.LITERAL, "3") } // Markdown, which has a scanner of its own: what a character means there is decided by where // it sits rather than by what it is, so most of these are about the cases where it means // nothing at all. @Test fun `a heading is coloured whole and a hash inside a word is not one`() { val code = "## Layout\nissue #12 is fixed\n#hashtag" assertSpans(code, Language.MARKDOWN, Kind.KEYWORD, "## Layout") } @Test fun `seven hashes are not a heading`() { assertSpans("####### deep", Language.MARKDOWN, Kind.KEYWORD) } @Test fun `a fence carries its language as metadata and its body as one string`() { val code = "text\n```kotlin\nval x = 1\n```\nmore" assertSpans(code, Language.MARKDOWN, Kind.METADATA, "kotlin") assertSpans(code, Language.MARKDOWN, Kind.STRING, "```", "val x = 1", "```") } /** The state that crosses a line, so the one worth asking about at both ends. */ @Test fun `a longer fence is not closed by a shorter one, and a heading inside it is not a heading`() { val code = "````\n```\n# not a heading\n````\nafter" assertSpans(code, Language.MARKDOWN, Kind.KEYWORD) assertSpans(code, Language.MARKDOWN, Kind.STRING, "````", "```", "# not a heading", "````") } @Test fun `an unclosed fence runs to the end rather than throwing`() { assertSpans("```\nstill going", Language.MARKDOWN, Kind.STRING, "```", "still going") } @Test fun `list markers and quote markers colour without their text`() { val code = "- one\n2. two\n> quoted" assertSpans(code, Language.MARKDOWN, Kind.MARK, "-", "2.", ">") } @Test fun `a rule and a setext underline are the same mark`() { assertSpans("Title\n=====\n\n---", Language.MARKDOWN, Kind.MARK, "=====", "---") } @Test fun `emphasis needs something on both sides of it`() { assertSpans("**bold** and *thin*", Language.MARKDOWN, Kind.LITERAL, "**bold**", "*thin*") // The case the guards exist for: a C fragment written in a paragraph. assertSpans("a * b * c and *p = *q", Language.MARKDOWN, Kind.LITERAL) } @Test fun `an underscore inside a word emphasises nothing`() { assertSpans("snake_case_name and _real_", Language.MARKDOWN, Kind.LITERAL, "_real_") } @Test fun `a code span holds a backtick when opened with two`() { assertSpans("``a ` b`` and `c`", Language.MARKDOWN, Kind.STRING, "``a ` b``", "`c`") } @Test fun `an unclosed code span is ordinary text`() { assertSpans("a ` b", Language.MARKDOWN, Kind.STRING) } @Test fun `a link marks its brackets and colours its destination`() { val code = "see [the plan](PLAN.md) now" assertSpans(code, Language.MARKDOWN, Kind.MARK, "[", "]") assertSpans(code, Language.MARKDOWN, Kind.METADATA, "(PLAN.md)") } @Test fun `a table is found by its delimiter row, and pipes elsewhere are plain`() { val code = "| a | b |\n|---|---|\n| 1 | 2 |\n\nrun a | b in a paragraph" assertSpans( code, Language.MARKDOWN, Kind.MARK, "|", "|", "|", "|---|---|", "|", "|", "|", ) } @Test fun `a table without outer pipes still colours, and the table ends with the rows`() { val code = "a | b\n--- | ---\nnot a row" assertSpans(code, Language.MARKDOWN, Kind.MARK, "|", "--- | ---") } /** * The tag in the last case is not an autolink and is left plain, but the address inside it is * still an address and the bare-URL pass finds it. That is the intended reading: raw HTML is * not something this scanner knows, and a URL is a URL wherever it was written. */ @Test fun `an autolink colours and an HTML tag does not`() { val code = " and and
and " assertSpans( code, Language.MARKDOWN, Kind.METADATA, "", "", "http://x", ) } @Test fun `a bare URL gives back the sentence's punctuation`() { assertSpans( "see https://example.com/a., and ssh://host/x)", Language.MARKDOWN, Kind.METADATA, "https://example.com/a", "ssh://host/x", ) } @Test fun `a bracket a URL opened itself stays in it`() { assertSpans( "https://en.wikipedia.org/wiki/A_(b) here", Language.MARKDOWN, Kind.METADATA, "https://en.wikipedia.org/wiki/A_(b)", ) } @Test fun `a URL inside a link destination is not coloured twice`() { assertSpans( "[x](https://example.com)", Language.MARKDOWN, Kind.METADATA, "(https://example.com)", ) } @Test fun `a bracket with no destination after it is left plain`() { assertSpans("an [aside] here", Language.MARKDOWN, Kind.MARK) } @Test fun `an unknown fence language is drawn plain`() { assertEquals(null, fenceLanguage("brainfuck")) assertEquals("+[-]", highlight("+[-]", fenceLanguage("brainfuck")).text) } @Test fun `every language the fence table knows has a scanner`() { Language.entries.forEach { spansOf("x", it) } } /** * The scanner must never throw and must never answer a span the code does not contain: the * library's reversed range is exactly the shape that crashed a card, and a fence still being * written is an unterminated string or comment on every keystroke. */ @Test fun `spans stay inside the code for every language and every nasty input`() { val nasty = listOf( "", "'", "\"", "\"unterminated", "/* unterminated", "###", "#", "#![", "[", "r#\"", "\\", "'''", "\"\"\"", "0x", "1.2.3", "a#b//c/*d*/'e\"f", "```", "*", "**", "~~", "> ", "- ", "1.", "[x](", "#######", "|", "|---|", "<", "<>", "http://", "a://", "\n\n \n", ) for (language in Language.entries) { for (code in nasty) { val spans = spansOf(code, language) spans.forEach { assertTrue( it.start in 0..it.end && it.end <= code.length, "$language answered $it for ${code.replace("\n", "\\n")}", ) } assertEquals( spans.sortedBy { it.start }, spans, "$language answered spans out of order for ${code.replace("\n", "\\n")}", ) } } } }