Initial support to variables and to producer/consumer logic
This commit is contained in:
45
test/formatter_variables_test.go
Normal file
45
test/formatter_variables_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/marte-community/marte-dev-tools/internal/formatter"
|
||||
"github.com/marte-community/marte-dev-tools/internal/parser"
|
||||
)
|
||||
|
||||
func TestFormatterVariables(t *testing.T) {
|
||||
content := `
|
||||
#var MyInt: int = 10
|
||||
#var MyStr: string | "A" = "default"
|
||||
|
||||
+Obj = {
|
||||
Field1 = $MyInt
|
||||
Field2 = $MyStr
|
||||
}
|
||||
`
|
||||
p := parser.NewParser(content)
|
||||
cfg, err := p.Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed: %v", err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
formatter.Format(cfg, &buf)
|
||||
|
||||
output := buf.String()
|
||||
|
||||
// Parser reconstructs type expression with spaces
|
||||
if !strings.Contains(output, "#var MyInt: int = 10") {
|
||||
t.Errorf("Variable MyInt formatted incorrectly. Got:\n%s", output)
|
||||
}
|
||||
// Note: parser adds space after each token in TypeExpr
|
||||
// string | "A" -> "string | \"A\""
|
||||
if !strings.Contains(output, "#var MyStr: string | \"A\" = \"default\"") {
|
||||
t.Errorf("Variable MyStr formatted incorrectly. Got:\n%s", output)
|
||||
}
|
||||
if !strings.Contains(output, "Field1 = $MyInt") {
|
||||
t.Errorf("Variable reference $MyInt formatted incorrectly. Got:\n%s", output)
|
||||
}
|
||||
}
|
||||
73
test/lsp_inout_test.go
Normal file
73
test/lsp_inout_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/marte-community/marte-dev-tools/internal/index"
|
||||
"github.com/marte-community/marte-dev-tools/internal/lsp"
|
||||
"github.com/marte-community/marte-dev-tools/internal/schema"
|
||||
)
|
||||
|
||||
func TestLSPINOUTOrdering(t *testing.T) {
|
||||
lsp.Tree = index.NewProjectTree()
|
||||
lsp.Documents = make(map[string]string)
|
||||
// Mock schema if necessary, but we rely on internal schema
|
||||
lsp.GlobalSchema = schema.LoadFullSchema(".")
|
||||
|
||||
var buf bytes.Buffer
|
||||
lsp.Output = &buf
|
||||
|
||||
content := `
|
||||
+App = {
|
||||
Class = RealTimeApplication
|
||||
+Data = {
|
||||
Class = ReferenceContainer
|
||||
+DDB = {
|
||||
Class = GAMDataSource
|
||||
}
|
||||
}
|
||||
+Functions = {
|
||||
Class = ReferenceContainer
|
||||
+A = {
|
||||
Class = IOGAM
|
||||
InputSignals = {
|
||||
A = {
|
||||
DataSource = DDB
|
||||
Type = uint32
|
||||
}
|
||||
}
|
||||
OutputSignals = {
|
||||
B = {
|
||||
DataSource = DDB
|
||||
Type = uint32
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+States = {
|
||||
Class = ReferenceContainer
|
||||
+State = {
|
||||
Class =RealTimeState
|
||||
Threads = {
|
||||
+Th1 = {
|
||||
Class = RealTimeThread
|
||||
Functions = {A}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
uri := "file://app.marte"
|
||||
lsp.HandleDidOpen(lsp.DidOpenTextDocumentParams{
|
||||
TextDocument: lsp.TextDocumentItem{URI: uri, Text: content},
|
||||
})
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "INOUT Signal 'A'") {
|
||||
t.Error("LSP did not report INOUT ordering error")
|
||||
t.Log(output)
|
||||
}
|
||||
}
|
||||
66
test/lsp_inout_warning_test.go
Normal file
66
test/lsp_inout_warning_test.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/marte-community/marte-dev-tools/internal/index"
|
||||
"github.com/marte-community/marte-dev-tools/internal/lsp"
|
||||
"github.com/marte-community/marte-dev-tools/internal/schema"
|
||||
)
|
||||
|
||||
func TestLSPINOUTWarning(t *testing.T) {
|
||||
lsp.Tree = index.NewProjectTree()
|
||||
lsp.Documents = make(map[string]string)
|
||||
lsp.GlobalSchema = schema.LoadFullSchema(".")
|
||||
|
||||
var buf bytes.Buffer
|
||||
lsp.Output = &buf
|
||||
|
||||
content := `
|
||||
+App = {
|
||||
Class = RealTimeApplication
|
||||
+Data = {
|
||||
Class = ReferenceContainer
|
||||
+DDB = {
|
||||
Class = GAMDataSource
|
||||
}
|
||||
}
|
||||
+Functions = {
|
||||
Class = ReferenceContainer
|
||||
+Producer = {
|
||||
Class = IOGAM
|
||||
OutputSignals = {
|
||||
ProducedSig = {
|
||||
DataSource = DDB
|
||||
Type = uint32
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+States = {
|
||||
Class = ReferenceContainer
|
||||
+State = {
|
||||
Class =RealTimeState
|
||||
Threads = {
|
||||
+Th1 = {
|
||||
Class = RealTimeThread
|
||||
Functions = {Producer}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
uri := "file://warning.marte"
|
||||
lsp.HandleDidOpen(lsp.DidOpenTextDocumentParams{
|
||||
TextDocument: lsp.TextDocumentItem{URI: uri, Text: content},
|
||||
})
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "produced in thread '+Th1' but never consumed") {
|
||||
t.Error("LSP did not report INOUT usage warning")
|
||||
t.Log(output)
|
||||
}
|
||||
}
|
||||
93
test/validator_inout_ordering_test.go
Normal file
93
test/validator_inout_ordering_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
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 TestINOUTOrdering(t *testing.T) {
|
||||
content := `
|
||||
+Data = {
|
||||
Class = ReferenceContainer
|
||||
+MyDS = {
|
||||
Class = GAMDataSource
|
||||
#meta = { multithreaded = false } // Explicitly false
|
||||
Signals = { Sig1 = { Type = uint32 } }
|
||||
}
|
||||
}
|
||||
+GAM_Consumer = {
|
||||
Class = IOGAM
|
||||
InputSignals = {
|
||||
Sig1 = { DataSource = MyDS Type = uint32 }
|
||||
}
|
||||
}
|
||||
+GAM_Producer = {
|
||||
Class = IOGAM
|
||||
OutputSignals = {
|
||||
Sig1 = { DataSource = MyDS Type = uint32 }
|
||||
}
|
||||
}
|
||||
+App = {
|
||||
Class = RealTimeApplication
|
||||
+States = {
|
||||
Class = ReferenceContainer
|
||||
+State1 = {
|
||||
Class = RealTimeState
|
||||
+Thread1 = {
|
||||
Class = RealTimeThread
|
||||
Functions = { GAM_Consumer, GAM_Producer } // Fail
|
||||
}
|
||||
}
|
||||
+State2 = {
|
||||
Class = RealTimeState
|
||||
+Thread2 = {
|
||||
Class = RealTimeThread
|
||||
Functions = { GAM_Producer, GAM_Consumer } // Pass
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
pt := index.NewProjectTree()
|
||||
p := parser.NewParser(content)
|
||||
cfg, err := p.Parse()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pt.AddFile("main.marte", cfg)
|
||||
|
||||
// Use validator with default schema (embedded)
|
||||
// We pass "." but it shouldn't matter if no .marte_schema.cue exists
|
||||
v := validator.NewValidator(pt, ".")
|
||||
v.ValidateProject()
|
||||
|
||||
foundError := false
|
||||
for _, d := range v.Diagnostics {
|
||||
if strings.Contains(d.Message, "consumed by GAM '+GAM_Consumer'") &&
|
||||
strings.Contains(d.Message, "before being produced") {
|
||||
foundError = true
|
||||
}
|
||||
}
|
||||
|
||||
if !foundError {
|
||||
t.Error("Expected INOUT ordering error for State1")
|
||||
for _, d := range v.Diagnostics {
|
||||
t.Logf("Diag: %s", d.Message)
|
||||
}
|
||||
}
|
||||
|
||||
foundErrorState2 := false
|
||||
for _, d := range v.Diagnostics {
|
||||
if strings.Contains(d.Message, "State '+State2'") && strings.Contains(d.Message, "before being produced") {
|
||||
foundErrorState2 = true
|
||||
}
|
||||
}
|
||||
|
||||
if foundErrorState2 {
|
||||
t.Error("Unexpected INOUT ordering error for State2 (Correct order)")
|
||||
}
|
||||
}
|
||||
72
test/variables_test.go
Normal file
72
test/variables_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/marte-community/marte-dev-tools/internal/builder"
|
||||
"github.com/marte-community/marte-dev-tools/internal/parser"
|
||||
)
|
||||
|
||||
func TestVariables(t *testing.T) {
|
||||
content := `
|
||||
#var MyInt: int = 10
|
||||
#var MyStr: string = "default"
|
||||
|
||||
+Obj = {
|
||||
Class = Test
|
||||
Field1 = $MyInt
|
||||
Field2 = $MyStr
|
||||
}
|
||||
`
|
||||
// Test Parsing
|
||||
p := parser.NewParser(content)
|
||||
cfg, err := p.Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed: %v", err)
|
||||
}
|
||||
|
||||
// Check definitions: #var, #var, +Obj
|
||||
if len(cfg.Definitions) != 3 {
|
||||
t.Errorf("Expected 3 definitions, got %d", len(cfg.Definitions))
|
||||
}
|
||||
|
||||
// Test Builder resolution
|
||||
f, _ := os.CreateTemp("", "vars.marte")
|
||||
f.WriteString(content)
|
||||
f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
// Build with override
|
||||
overrides := map[string]string{
|
||||
"MyInt": "999",
|
||||
}
|
||||
|
||||
b := builder.NewBuilder([]string{f.Name()}, overrides)
|
||||
|
||||
outF, _ := os.CreateTemp("", "out.marte")
|
||||
outName := outF.Name()
|
||||
defer os.Remove(outName)
|
||||
|
||||
err = b.Build(outF)
|
||||
outF.Close()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Build failed: %v", err)
|
||||
}
|
||||
|
||||
outContent, _ := os.ReadFile(outName)
|
||||
outStr := string(outContent)
|
||||
|
||||
if !strings.Contains(outStr, "Field1 = 999") {
|
||||
t.Errorf("Variable override failed for MyInt. Got:\n%s", outStr)
|
||||
}
|
||||
if !strings.Contains(outStr, "Field2 = \"default\"") {
|
||||
t.Errorf("Default value failed for MyStr. Got:\n%s", outStr)
|
||||
}
|
||||
// Check #var is removed
|
||||
if strings.Contains(outStr, "#var") {
|
||||
t.Error("#var definition present in output")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user