Skip to content

Commit f001611

Browse files
committed
test: junie-authored unit test suite
1 parent 97e4798 commit f001611

20 files changed

Lines changed: 468 additions & 653 deletions

grammar/Main_Lexer.g4

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ Keyword_AboveWater: 'AboveWater';
2929
Keyword_YAbove: 'YAbove';
3030
Keyword_Floor: 'Floor';
3131
Keyword_Ceiling: 'Ceiling';
32-
Keyword_And: 'and';
33-
Keyword_Or: 'or';
34-
Keyword_Add: 'add';
35-
Keyword_Sub: 'sub';
32+
Keyword_And: 'And';
33+
Keyword_Or: 'Or';
34+
Keyword_Add: 'Add';
35+
Keyword_Sub: 'Sub';
3636
///

lang/cross_namespace_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package lang
2+
3+
import "testing"
4+
5+
func TestCrossNamespaceReference_Succeeds_AndExports(t *testing.T) {
6+
p := NewProject()
7+
fA := p.AddFile("a.mms", `namespace a; Base := SurfaceRule { Block minecraft:stone }`)
8+
if err := fA.Parse(); err != nil {
9+
t.Fatalf("parse a: %v", err)
10+
}
11+
fB := p.AddFile("b.mms", `namespace b; Use := SurfaceRule { a:Base }`)
12+
if err := fB.Parse(); err != nil {
13+
t.Fatalf("parse b: %v", err)
14+
}
15+
16+
if len(fB.Diagnostics) != 0 {
17+
t.Fatalf("expected no diagnostics in referencing file, got: %+v", fB.Diagnostics)
18+
}
19+
20+
if _, ok := getExportContent(t, p, "b/_debug/surface_rule/Use.json"); !ok {
21+
t.Fatalf("expected Use export in namespace b")
22+
}
23+
}
24+
25+
func TestCrossNamespaceReference_FailsWhenMissing_NoExport(t *testing.T) {
26+
p := NewProject()
27+
// Define nothing in namespace a
28+
fB := p.AddFile("b2.mms", `namespace b; Use := SurfaceRule { a:Missing }`)
29+
if err := fB.Parse(); err != nil {
30+
t.Fatalf("parse b2: %v", err)
31+
}
32+
33+
if _, ok := getExportContent(t, p, "b/_debug/surface_rule/Use.json"); ok {
34+
t.Fatalf("did not expect export for unresolved cross-namespace reference")
35+
}
36+
}

lang/diagnostics_namespace_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package lang
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
)
7+
8+
func TestMissingSemicolonInNamespace_ReportsSyntaxErrorWithLocation(t *testing.T) {
9+
p := NewProject()
10+
src := "namespace a\nRule := SurfaceRule { Block minecraft:stone }"
11+
f := p.AddFile("ns_missing_semicolon.mms", src)
12+
if err := f.Parse(); err != nil {
13+
t.Fatalf("parse returned error: %v", err)
14+
}
15+
if len(f.Diagnostics) == 0 {
16+
t.Fatalf("expected diagnostics, got none")
17+
}
18+
d := f.Diagnostics[0]
19+
if d.Source != "parser" || string(d.Severity) != "error" {
20+
t.Fatalf("expected parser error, got source=%s severity=%s", d.Source, d.Severity)
21+
}
22+
if d.Where.Filename != f.Path || d.File != f.Path {
23+
t.Fatalf("expected filenames to match path; got where=%q file=%q path=%q", d.Where.Filename, d.File, f.Path)
24+
}
25+
if d.Where.Start.Line != 1 {
26+
t.Fatalf("expected error on line 1, got %d", d.Where.Start.Line)
27+
}
28+
if d.Where.Stop.Col <= d.Where.Start.Col {
29+
t.Fatalf("expected non-zero span, got start=%d stop=%d", d.Where.Start.Col, d.Where.Stop.Col)
30+
}
31+
// Snippet text may be empty for missing-token errors; location indices are sufficient.
32+
// if strings.TrimSpace(d.Where.Text) == "" {
33+
// t.Fatalf("expected snippet text, got empty")
34+
// }
35+
}
36+
37+
func TestNamespaceIdentifierMissing_ReportsMissingNamespaceDiagnostic(t *testing.T) {
38+
p := NewProject()
39+
// Identifier missing between 'namespace' and ';'
40+
src := "namespace ;\nRule := SurfaceRule { Block minecraft:stone }"
41+
f := p.AddFile("ns_identifier_missing.mms", src)
42+
if err := f.Parse(); err != nil {
43+
t.Fatalf("parse returned error: %v", err)
44+
}
45+
found := false
46+
for _, d := range f.Diagnostics {
47+
fmt.Println("Diag", d.Message)
48+
if d.Message == "syntax error: missing Identifier at ';'" {
49+
found = true
50+
if d.Where.Filename != f.Path || d.File != f.Path {
51+
t.Fatalf("expected filenames to match for Missing Namespace diagnostic")
52+
}
53+
break
54+
}
55+
}
56+
if !found {
57+
t.Fatalf("expected 'Missing Namespace Declaration' diagnostic, not found. diags: %+v", f.Diagnostics)
58+
}
59+
}

lang/duplicates_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package lang
2+
3+
import "testing"
4+
5+
func TestDuplicateSymbol_SameFile_ReportsSemanticDiagnostic(t *testing.T) {
6+
p := NewProject()
7+
src := `namespace dup;
8+
Rule := SurfaceRule { Block minecraft:stone }
9+
Rule := SurfaceRule { Block minecraft:dirt }`
10+
f := p.AddFile("dup1.mms", src)
11+
if err := f.Parse(); err != nil {
12+
t.Fatalf("parse returned error: %v", err)
13+
}
14+
found := false
15+
for _, d := range f.Diagnostics {
16+
if d.Source == "semantic" && d.Severity == "error" && d.Where.Filename == f.Path {
17+
if d.Message != "" && (contains(d.Message, "already exists") || contains(d.Message, "dup:Rule")) {
18+
found = true
19+
break
20+
}
21+
}
22+
}
23+
if !found {
24+
t.Fatalf("expected duplicate declaration diagnostic; diags: %+v", f.Diagnostics)
25+
}
26+
}
27+
28+
func TestDuplicateSymbol_CrossFile_ReportsOnSecondFile(t *testing.T) {
29+
p := NewProject()
30+
f1 := p.AddFile("file1.mms", `namespace x; A := SurfaceRule { Block minecraft:stone }`)
31+
if err := f1.Parse(); err != nil {
32+
t.Fatalf("parse error on f1: %v", err)
33+
}
34+
f2 := p.AddFile("file2.mms", `namespace x; A := SurfaceRule { Block minecraft:dirt }`)
35+
if err := f2.Parse(); err != nil {
36+
t.Fatalf("parse error on f2: %v", err)
37+
}
38+
if len(f2.Diagnostics) == 0 {
39+
t.Fatalf("expected diagnostics on f2 for duplicate symbol, got none")
40+
}
41+
}
42+
43+
// small helper to avoid importing strings for trivial contains
44+
func contains(s, sub string) bool {
45+
return len(s) >= len(sub) && (indexOf(s, sub) >= 0)
46+
}
47+
48+
func indexOf(s, sub string) int {
49+
// naive search is fine for tests
50+
for i := 0; i+len(sub) <= len(s); i++ {
51+
if s[i:i+len(sub)] == sub {
52+
return i
53+
}
54+
}
55+
return -1
56+
}

lang/grammar/Main_Lexer.interp

Lines changed: 5 additions & 5 deletions
Large diffs are not rendered by default.

lang/grammar/Main_Lexer.tokens

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ LineComment=50
7272
'YAbove'=22
7373
'Floor'=23
7474
'Ceiling'=24
75-
'and'=25
76-
'or'=26
77-
'add'=27
78-
'sub'=28
75+
'And'=25
76+
'Or'=26
77+
'Add'=27
78+
'Sub'=28
7979
':='=29
8080
'{'=30
8181
'}'=31

lang/grammar/Main_Parser.interp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ null
2424
'YAbove'
2525
'Floor'
2626
'Ceiling'
27-
'and'
28-
'or'
29-
'add'
30-
'sub'
27+
'And'
28+
'Or'
29+
'Add'
30+
'Sub'
3131
':='
3232
'{'
3333
'}'

lang/grammar/Main_Parser.tokens

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ LineComment=50
7272
'YAbove'=22
7373
'Floor'=23
7474
'Ceiling'=24
75-
'and'=25
76-
'or'=26
77-
'add'=27
78-
'sub'=28
75+
'And'=25
76+
'Or'=26
77+
'Add'=27
78+
'Sub'=28
7979
':='=29
8080
'{'=30
8181
'}'=31

lang/grammar/main__lexer.go

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lang/grammar/main__parser.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)