package pythonlang import ( "strings" "testing" "rickub.com/turbo-editors/turbo-core/syntax" ) // TestTheLanguagesReferenceIsTrue holds docs/*/reference/languages.md to the // scanner. Every row of its Python table that no other test here covers is // checked, so a reference claim and the code cannot drift apart quietly. // // The last three cases document *limitations* rather than features — an // all-capital class name read as a constant, `type` read as the builtin rather // than as a soft keyword, an f-string's braces left inside the string. The // reference says each of those in so many words, and these rows are what stops // somebody "fixing" one without also fixing the sentence. func TestTheLanguagesReferenceIsTrue(t *testing.T) { tests := []struct { src string word string want syntax.Class }{ {"assert x is not None", "assert", syntax.ClassKeyword}, {"nonlocal counter", "nonlocal", syntax.ClassKeyword}, {"async def f() -> None:\n pass\n", "async", syntax.ClassKeyword}, {"x = NotImplemented", "NotImplemented", syntax.ClassConstant}, {"x = Ellipsis", "Ellipsis", syntax.ClassConstant}, {"if __debug__:\n pass\n", "__debug__", syntax.ClassConstant}, {"x: frozenset = frozenset()", "frozenset", syntax.ClassType}, {"x: memoryview = m", "memoryview", syntax.ClassType}, {"x = isinstance(v, int)", "isinstance", syntax.ClassBuiltin}, {"x = sorted(v)", "sorted", syntax.ClassBuiltin}, {"def f(cls) -> None:\n pass\n", "cls", syntax.ClassBuiltin}, {"x = obj.__name__", "__name__", syntax.ClassBuiltin}, {`x = rb"raw bytes"`, `rb"raw bytes"`, syntax.ClassString}, {`x = BR"raw bytes"`, `BR"raw bytes"`, syntax.ClassString}, {`x = u"text"`, `u"text"`, syntax.ClassString}, {"x = 0o17", "0o17", syntax.ClassNumber}, {"x = 0b1010", "0b1010", syntax.ClassNumber}, {"x = 3j", "3j", syntax.ClassNumber}, {"x = 1E+7", "1E+7", syntax.ClassNumber}, {"#!/usr/bin/env python3", "#!/usr/bin/env python3", syntax.ClassComment}, {"@property\ndef x(self):\n pass\n", "@property", syntax.ClassAttribute}, {"if (n := f()) > 1:\n pass\n", ":=", syntax.ClassOperator}, {"d = {1: 2}", ":", syntax.ClassPunctuation}, {"x = a @ b", "@", syntax.ClassOperator}, {"x = 1 + \\", "\\", syntax.ClassPunctuation}, {"x = [1, 2]", "[", syntax.ClassPunctuation}, // The documented limitations. {"HTTP = 1", "HTTP", syntax.ClassConstant}, {"type Alias = int", "type", syntax.ClassType}, {"match value: # dispatch\n", "match", syntax.ClassIdentifier}, } for _, test := range tests { t.Run(test.word, func(t *testing.T) { index := strings.Index(test.src, test.word) if index < 0 { t.Fatalf("%q not in %q", test.word, test.src) } got, ok := classAt(Highlight(test.src), 0, index) if !ok || got != test.want { t.Errorf("%q in %q is %v (covered %v), want %v", test.word, test.src, got, ok, test.want) } }) } } // The reference's Python table says the class `char` is produced by nothing // here, because Python has no character type. That is a claim about every span // the scanner can ever emit, so it is checked over a file that uses every // construct the table names. func TestTheScannerNeverProducesACharacterClass(t *testing.T) { const src = "#!/usr/bin/env python3\n" + "x = 'single'\n" + "y = \"double\"\n" + "z = b'bytes'\n" + "w = '''triple'''\n" for line, spans := range Highlight(src) { for _, span := range spans { if span.Class == syntax.ClassChar { t.Errorf("line %d holds a char span at %d; Python has no character literal", line, span.Start) } } } }