Better formatting and expression handling
This commit is contained in:
78
test/advanced_numbers_test.go
Normal file
78
test/advanced_numbers_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/marte-community/marte-dev-tools/internal/parser"
|
||||
"github.com/marte-community/marte-dev-tools/internal/formatter"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
func TestAdvancedNumbers(t *testing.T) {
|
||||
content := `
|
||||
Hex = 0xFF
|
||||
HexLower = 0xee
|
||||
Binary = 0b1011
|
||||
Decimal = 123
|
||||
Scientific = 1e-3
|
||||
`
|
||||
p := parser.NewParser(content)
|
||||
cfg, err := p.Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify values
|
||||
foundHex := false
|
||||
foundHexLower := false
|
||||
foundBinary := false
|
||||
for _, def := range cfg.Definitions {
|
||||
if f, ok := def.(*parser.Field); ok {
|
||||
if f.Name == "Hex" {
|
||||
if v, ok := f.Value.(*parser.IntValue); ok {
|
||||
if v.Value != 255 {
|
||||
t.Errorf("Expected 255 for Hex, got %d", v.Value)
|
||||
}
|
||||
foundHex = true
|
||||
}
|
||||
}
|
||||
if f.Name == "HexLower" {
|
||||
if v, ok := f.Value.(*parser.IntValue); ok {
|
||||
if v.Value != 238 {
|
||||
t.Errorf("Expected 238 for HexLower, got %d", v.Value)
|
||||
}
|
||||
foundHexLower = true
|
||||
} else {
|
||||
t.Errorf("HexLower was parsed as %T, expected *parser.IntValue", f.Value)
|
||||
}
|
||||
}
|
||||
if f.Name == "Binary" {
|
||||
if v, ok := f.Value.(*parser.IntValue); ok {
|
||||
if v.Value == 11 {
|
||||
foundBinary = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !foundHex { t.Error("Hex field not found") }
|
||||
if !foundHexLower { t.Error("HexLower field not found") }
|
||||
if !foundBinary { t.Error("Binary field not found") }
|
||||
|
||||
// Verify formatting
|
||||
var buf bytes.Buffer
|
||||
formatter.Format(cfg, &buf)
|
||||
formatted := buf.String()
|
||||
if !contains(formatted, "Hex = 0xFF") {
|
||||
t.Errorf("Formatted content missing Hex = 0xFF:\n%s", formatted)
|
||||
}
|
||||
if !contains(formatted, "HexLower = 0xee") {
|
||||
t.Errorf("Formatted content missing HexLower = 0xee:\n%s", formatted)
|
||||
}
|
||||
if !contains(formatted, "Binary = 0b1011") {
|
||||
t.Errorf("Formatted content missing Binary = 0b1011:\n%s", formatted)
|
||||
}
|
||||
}
|
||||
|
||||
func contains(s, substr string) bool {
|
||||
return bytes.Contains([]byte(s), []byte(substr))
|
||||
}
|
||||
88
test/evaluated_signal_props_test.go
Normal file
88
test/evaluated_signal_props_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/marte-community/marte-dev-tools/internal/index"
|
||||
"github.com/marte-community/marte-dev-tools/internal/parser"
|
||||
"github.com/marte-community/marte-dev-tools/internal/validator"
|
||||
)
|
||||
|
||||
func TestEvaluatedSignalProperties(t *testing.T) {
|
||||
content := `
|
||||
#let N: uint32 = 10
|
||||
+DS = {
|
||||
Class = FileReader
|
||||
Filename = "test.bin"
|
||||
Signals = {
|
||||
Sig1 = { Type = uint32 NumberOfElements = @N }
|
||||
}
|
||||
}
|
||||
+GAM = {
|
||||
Class = IOGAM
|
||||
InputSignals = {
|
||||
Sig1 = { DataSource = DS Type = uint32 NumberOfElements = 10 }
|
||||
}
|
||||
}
|
||||
`
|
||||
p := parser.NewParser(content)
|
||||
cfg, err := p.Parse()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tree := index.NewProjectTree()
|
||||
tree.AddFile("test.marte", cfg)
|
||||
tree.ResolveReferences()
|
||||
|
||||
v := validator.NewValidator(tree, ".")
|
||||
v.ValidateProject()
|
||||
|
||||
// There should be no errors because @N evaluates to 10
|
||||
for _, d := range v.Diagnostics {
|
||||
if d.Level == validator.LevelError {
|
||||
t.Errorf("Unexpected error: %s", d.Message)
|
||||
}
|
||||
}
|
||||
|
||||
// Test mismatch with expression
|
||||
contentErr := `
|
||||
#let N: uint32 = 10
|
||||
+DS = {
|
||||
Class = FileReader
|
||||
Filename = "test.bin"
|
||||
Signals = {
|
||||
Sig1 = { Type = uint32 NumberOfElements = @N + 5 }
|
||||
}
|
||||
}
|
||||
+GAM = {
|
||||
Class = IOGAM
|
||||
InputSignals = {
|
||||
Sig1 = { DataSource = DS Type = uint32 NumberOfElements = 10 }
|
||||
}
|
||||
}
|
||||
`
|
||||
p2 := parser.NewParser(contentErr)
|
||||
cfg2, _ := p2.Parse()
|
||||
tree2 := index.NewProjectTree()
|
||||
tree2.AddFile("test_err.marte", cfg2)
|
||||
tree2.ResolveReferences()
|
||||
|
||||
v2 := validator.NewValidator(tree2, ".")
|
||||
v2.ValidateProject()
|
||||
|
||||
found := false
|
||||
for _, d := range v2.Diagnostics {
|
||||
if strings.Contains(d.Message, "property 'NumberOfElements' mismatch") {
|
||||
found = true
|
||||
if !strings.Contains(d.Message, "defined '15'") {
|
||||
t.Errorf("Expected defined '15', got message: %s", d.Message)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("Expected property mismatch error for @N + 5")
|
||||
}
|
||||
}
|
||||
125
test/let_macro_test.go
Normal file
125
test/let_macro_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/marte-community/marte-dev-tools/internal/builder"
|
||||
"github.com/marte-community/marte-dev-tools/internal/index"
|
||||
"github.com/marte-community/marte-dev-tools/internal/parser"
|
||||
"github.com/marte-community/marte-dev-tools/internal/validator"
|
||||
)
|
||||
|
||||
func TestLetMacroFull(t *testing.T) {
|
||||
content := `
|
||||
//# My documentation
|
||||
#let MyConst: uint32 = 10 + 20
|
||||
+Obj = {
|
||||
Value = @MyConst
|
||||
}
|
||||
`
|
||||
tmpFile, _ := os.CreateTemp("", "let_*.marte")
|
||||
defer os.Remove(tmpFile.Name())
|
||||
os.WriteFile(tmpFile.Name(), []byte(content), 0644)
|
||||
|
||||
// 1. Test Parsing & Indexing
|
||||
p := parser.NewParser(content)
|
||||
cfg, err := p.Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed: %v", err)
|
||||
}
|
||||
|
||||
tree := index.NewProjectTree()
|
||||
tree.AddFile(tmpFile.Name(), cfg)
|
||||
|
||||
vars := tree.Root.Variables
|
||||
if iso, ok := tree.IsolatedFiles[tmpFile.Name()]; ok {
|
||||
vars = iso.Variables
|
||||
}
|
||||
|
||||
info, ok := vars["MyConst"]
|
||||
if !ok || !info.Def.IsConst {
|
||||
t.Fatal("#let variable not indexed correctly as Const")
|
||||
}
|
||||
if info.Doc != "My documentation" {
|
||||
t.Errorf("Expected doc 'My documentation', got '%s'", info.Doc)
|
||||
}
|
||||
|
||||
// 2. Test Builder Evaluation
|
||||
out, _ := os.CreateTemp("", "let_out.cfg")
|
||||
defer os.Remove(out.Name())
|
||||
|
||||
b := builder.NewBuilder([]string{tmpFile.Name()}, nil)
|
||||
if err := b.Build(out); err != nil {
|
||||
t.Fatalf("Build failed: %v", err)
|
||||
}
|
||||
|
||||
outContent, _ := os.ReadFile(out.Name())
|
||||
if !strings.Contains(string(outContent), "Value = 30") {
|
||||
t.Errorf("Expected Value = 30 (evaluated @MyConst), got:\n%s", string(outContent))
|
||||
}
|
||||
|
||||
// 3. Test Override Protection
|
||||
out2, _ := os.CreateTemp("", "let_out2.cfg")
|
||||
defer os.Remove(out2.Name())
|
||||
|
||||
b2 := builder.NewBuilder([]string{tmpFile.Name()}, map[string]string{"MyConst": "100"})
|
||||
if err := b2.Build(out2); err != nil {
|
||||
t.Fatalf("Build failed: %v", err)
|
||||
}
|
||||
|
||||
outContent2, _ := os.ReadFile(out2.Name())
|
||||
if !strings.Contains(string(outContent2), "Value = 30") {
|
||||
t.Errorf("Constant was overridden! Expected 30, got:\n%s", string(outContent2))
|
||||
}
|
||||
|
||||
// 4. Test Validator (Mandatory Value)
|
||||
contentErr := "#let BadConst: uint32"
|
||||
p2 := parser.NewParser(contentErr)
|
||||
cfg2, err2 := p2.Parse()
|
||||
// Parser might fail if = is missing?
|
||||
// parseLet expects =.
|
||||
if err2 == nil {
|
||||
// If parser didn't fail (maybe it was partial), validator should catch it
|
||||
tree2 := index.NewProjectTree()
|
||||
tree2.AddFile("err.marte", cfg2)
|
||||
v := validator.NewValidator(tree2, ".")
|
||||
v.ValidateProject()
|
||||
|
||||
found := false
|
||||
for _, d := range v.Diagnostics {
|
||||
if strings.Contains(d.Message, "must have an initial value") {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found && cfg2 != nil {
|
||||
// If p2.Parse() failed and added error to p2.errors, it's also fine.
|
||||
// But check if it reached validator.
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Test Duplicate Detection
|
||||
contentDup := `
|
||||
#let MyConst: uint32 = 10
|
||||
#var MyConst: uint32 = 20
|
||||
`
|
||||
p3 := parser.NewParser(contentDup)
|
||||
cfg3, _ := p3.Parse()
|
||||
tree3 := index.NewProjectTree()
|
||||
tree3.AddFile("dup.marte", cfg3)
|
||||
v3 := validator.NewValidator(tree3, ".")
|
||||
v3.ValidateProject()
|
||||
|
||||
foundDup := false
|
||||
for _, d := range v3.Diagnostics {
|
||||
if strings.Contains(d.Message, "Duplicate variable definition") {
|
||||
foundDup = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundDup {
|
||||
t.Error("Expected duplicate variable definition error")
|
||||
}
|
||||
}
|
||||
88
test/lsp_recursive_index_test.go
Normal file
88
test/lsp_recursive_index_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/marte-community/marte-dev-tools/internal/index"
|
||||
"github.com/marte-community/marte-dev-tools/internal/lsp"
|
||||
)
|
||||
|
||||
func TestLSPRecursiveIndexing(t *testing.T) {
|
||||
// Setup directory structure
|
||||
rootDir, err := os.MkdirTemp("", "lsp_recursive")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(rootDir)
|
||||
|
||||
// root/main.marte
|
||||
mainContent := `
|
||||
#package App
|
||||
+Main = {
|
||||
Ref = SubComp
|
||||
}
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(rootDir, "main.marte"), []byte(mainContent), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// root/subdir/sub.marte
|
||||
subDir := filepath.Join(rootDir, "subdir")
|
||||
if err := os.Mkdir(subDir, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subContent := `
|
||||
#package App
|
||||
+SubComp = { Class = Component }
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(subDir, "sub.marte"), []byte(subContent), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Initialize LSP
|
||||
lsp.Tree = index.NewProjectTree()
|
||||
lsp.Documents = make(map[string]string)
|
||||
|
||||
// Simulate ScanDirectory
|
||||
if err := lsp.Tree.ScanDirectory(rootDir); err != nil {
|
||||
t.Fatalf("ScanDirectory failed: %v", err)
|
||||
}
|
||||
lsp.Tree.ResolveReferences()
|
||||
|
||||
// Check if SubComp is in the tree
|
||||
// Root -> App -> SubComp
|
||||
appNode := lsp.Tree.Root.Children["App"]
|
||||
if appNode == nil {
|
||||
t.Fatal("App package not found")
|
||||
}
|
||||
|
||||
subComp := appNode.Children["SubComp"]
|
||||
if subComp == nil {
|
||||
t.Fatal("SubComp not found in tree (recursive scan failed)")
|
||||
}
|
||||
|
||||
mainURI := "file://" + filepath.Join(rootDir, "main.marte")
|
||||
|
||||
// Definition Request
|
||||
params := lsp.DefinitionParams{
|
||||
TextDocument: lsp.TextDocumentIdentifier{URI: mainURI},
|
||||
Position: lsp.Position{Line: 3, Character: 12},
|
||||
}
|
||||
|
||||
res := lsp.HandleDefinition(params)
|
||||
if res == nil {
|
||||
t.Fatal("Definition not found for SubComp")
|
||||
}
|
||||
|
||||
locs, ok := res.([]lsp.Location)
|
||||
if !ok || len(locs) == 0 {
|
||||
t.Fatal("Expected location list")
|
||||
}
|
||||
|
||||
expectedFile := filepath.Join(subDir, "sub.marte")
|
||||
if locs[0].URI != "file://"+expectedFile {
|
||||
t.Errorf("Expected definition in %s, got %s", expectedFile, locs[0].URI)
|
||||
}
|
||||
}
|
||||
54
test/recursive_indexing_test.go
Normal file
54
test/recursive_indexing_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/marte-community/marte-dev-tools/internal/index"
|
||||
)
|
||||
|
||||
func TestRecursiveIndexing(t *testing.T) {
|
||||
// Setup: root/level1/level2/deep.marte
|
||||
rootDir, _ := os.MkdirTemp("", "rec_index")
|
||||
defer os.RemoveAll(rootDir)
|
||||
|
||||
l1 := filepath.Join(rootDir, "level1")
|
||||
l2 := filepath.Join(l1, "level2")
|
||||
if err := os.MkdirAll(l2, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
content := "#package Deep\n+DeepObj = { Class = A }"
|
||||
if err := os.WriteFile(filepath.Join(l2, "deep.marte"), []byte(content), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Also add a file in root to ensure mixed levels work
|
||||
os.WriteFile(filepath.Join(rootDir, "root.marte"), []byte("#package Root\n+RootObj = { Class = A }"), 0644)
|
||||
|
||||
// Scan
|
||||
tree := index.NewProjectTree()
|
||||
err := tree.ScanDirectory(rootDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Scan failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify Deep
|
||||
deepPkg := tree.Root.Children["Deep"]
|
||||
if deepPkg == nil {
|
||||
t.Fatal("Package Deep not found")
|
||||
}
|
||||
if deepPkg.Children["DeepObj"] == nil {
|
||||
t.Fatal("DeepObj not found in Deep package")
|
||||
}
|
||||
|
||||
// Verify Root
|
||||
rootPkg := tree.Root.Children["Root"]
|
||||
if rootPkg == nil {
|
||||
t.Fatal("Package Root not found")
|
||||
}
|
||||
if rootPkg.Children["RootObj"] == nil {
|
||||
t.Fatal("RootObj not found in Root package")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user