package dsl import ( "strings" "testing" ) func TestTokenStringAndUnknownType(t *testing.T) { tok := Token{Type: TOKEN_IDENT, Value: "foo", Line: 2, Col: 5} s := tok.String() if !strings.Contains(s, "IDENT") || !strings.Contains(s, "foo") { t.Errorf("Token.String() = %q", s) } if got := TokenType(999).String(); got != "UNKNOWN" { t.Errorf("TokenType(999).String() = %q, want UNKNOWN", got) } for tt, want := range map[TokenType]string{ TOKEN_STRING: "STRING", TOKEN_FLOAT: "FLOAT", TOKEN_INT: "INT", TOKEN_DURATION: "DURATION", TOKEN_SIZE: "SIZE", TOKEN_LBRACE: "{", TOKEN_RBRACE: "}", TOKEN_LPAREN: "(", TOKEN_RPAREN: ")", TOKEN_ASSIGN: "=", TOKEN_TILDE: "~", TOKEN_EOF: "EOF", } { if got := tt.String(); got != want { t.Errorf("TokenType(%d) = %q, want %q", tt, got, want) } } } func TestLexerLineComment(t *testing.T) { src := ` // leading comment build "make" // trailing group("g") { // inside weight = 1.0 test("t") { stdout = "" } } ` if _, _, err := Parse(src); err != nil { t.Errorf("parse with comments: %v", err) } } func TestLexerUnterminatedString(t *testing.T) { _, _, err := Parse(`build "unterminated`) if err == nil || !strings.Contains(err.Error(), "unterminated") { t.Errorf("want unterminated string error, got %v", err) } } func TestLexerUnknownEscape(t *testing.T) { _, _, err := Parse(`build "bad\zescape"`) if err == nil || !strings.Contains(err.Error(), "unknown escape") { t.Errorf("want unknown escape error, got %v", err) } } func TestLexerEscapeSequences(t *testing.T) { src := `build "a\nb\tc\\d\"e"` f, _, err := Parse(src + "\ngroup(\"g\") { weight = 1.0 test(\"t\") { stdout = \"\" } }") if err != nil { t.Fatalf("parse: %v", err) } if f.Build != "a\nb\tc\\d\"e" { t.Errorf("escape sequences = %q", f.Build) } } func TestLexerUnexpectedCharacter(t *testing.T) { _, _, err := Parse("build @invalid") if err == nil || !strings.Contains(err.Error(), "unexpected character") { t.Errorf("want unexpected character error, got %v", err) } } func TestLexerUnterminatedHeredoc(t *testing.T) { src := "build \"x\"\ngroup(\"g\") { weight = 1.0 test(\"t\") { stdin = \"\"\"\nnever closed\n } }" _, _, err := Parse(src) if err == nil || !strings.Contains(err.Error(), "unterminated heredoc") { t.Errorf("want unterminated heredoc, got %v", err) } } func TestLexerSizeSuffixes(t *testing.T) { cases := map[string]int64{ "1024": 1024, "1B": 1, "1K": 1024, "1KB": 1024, "1KiB": 1024, "1M": 1024 * 1024, "1MB": 1024 * 1024, "1MiB": 1024 * 1024, "1G": 1024 * 1024 * 1024, "1GB": 1024 * 1024 * 1024, "1GiB": 1024 * 1024 * 1024, } for literal, want := range cases { src := "build \"x\"\nmemory_limit = " + literal + "\ngroup(\"g\") { weight = 1.0 test(\"t\") { stdout = \"\" } }" f, _, err := Parse(src) if err != nil { t.Errorf("parse %q: %v", literal, err) continue } if f.MemoryLimit != want { t.Errorf("memory_limit %s = %d, want %d", literal, f.MemoryLimit, want) } } } func TestLexerDurationSuffixes(t *testing.T) { cases := []string{"5ms", "10s", "2m"} for _, d := range cases { src := "build \"x\"\ntimeout " + d + "\ngroup(\"g\") { weight = 1.0 test(\"t\") { stdout = \"\" } }" if _, _, err := Parse(src); err != nil { t.Errorf("parse timeout %s: %v", d, err) } } } func TestLexerNegativeInt(t *testing.T) { src := ` build "x" group("g") { weight = 1.0 test("t") { exitCode = -1 stdout = "" } } ` f, _, err := Parse(src) if err != nil { t.Fatalf("parse: %v", err) } tst := f.Groups[0].Tests[0] if tst.ExitCode == nil || *tst.ExitCode != -1 { t.Errorf("ExitCode = %v, want -1", tst.ExitCode) } } func TestLexerFloatNumber(t *testing.T) { src := ` build "x" group("g") { weight = 0.75 test("t") { stdout = "" } } group("g2") { weight = 0.25 test("t") { stdout = "" } } ` f, _, err := Parse(src) if err != nil { t.Fatalf("parse: %v", err) } if f.Groups[0].Weight != 0.75 { t.Errorf("weight = %v", f.Groups[0].Weight) } } func TestLexerInvalidSizeUnit(t *testing.T) { _, _, err := Parse("build \"x\"\nmemory_limit = 10Z\n") if err == nil { t.Error("expected error for invalid size unit") } }