Files
marte_dev_tools/test/validator_multifile_test.go
2026-01-23 10:23:02 +01:00

197 lines
5.4 KiB
Go

package integration
import (
"io/ioutil"
"strings"
"testing"
"github.com/marte-dev/marte-dev-tools/internal/index"
"github.com/marte-dev/marte-dev-tools/internal/parser"
"github.com/marte-dev/marte-dev-tools/internal/validator"
)
func parseAndAddToIndex(t *testing.T, idx *index.ProjectTree, filePath string) {
content, err := ioutil.ReadFile(filePath)
if err != nil {
t.Fatalf("Failed to read %s: %v", filePath, err)
}
p := parser.NewParser(string(content))
config, err := p.Parse()
if err != nil {
t.Fatalf("Parse failed for %s: %v", filePath, err)
}
idx.AddFile(filePath, config)
}
func TestMultiFileNodeValidation(t *testing.T) {
idx := index.NewProjectTree()
parseAndAddToIndex(t, idx, "integration/multifile_valid_1.marte")
parseAndAddToIndex(t, idx, "integration/multifile_valid_2.marte")
// Resolving references might be needed if the validator relies on it for merging implicitly
// But primarily we want to check if the validator sees the merged node.
// The current implementation of Validator likely iterates over the ProjectTree.
// If the ProjectTree doesn't merge nodes automatically, the Validator needs to do it.
// However, the spec says "The build tool, validator, and LSP must merge these definitions".
// Let's assume the Validator or Index does the merging logic.
v := validator.NewValidator(idx, ".")
v.ValidateProject()
// +MyNode is split.
// valid_1 has FieldA
// valid_2 has Class and FieldB
// If merging works, it should have a Class, so no error about missing Class.
for _, diag := range v.Diagnostics {
if strings.Contains(diag.Message, "must contain a 'Class' field") {
t.Errorf("Unexpected 'Class' field error for +MyNode: %s", diag.Message)
}
}
}
func TestMultiFileDuplicateField(t *testing.T) {
idx := index.NewProjectTree()
parseAndAddToIndex(t, idx, "integration/multifile_dup_1.marte")
parseAndAddToIndex(t, idx, "integration/multifile_dup_2.marte")
v := validator.NewValidator(idx, ".")
v.ValidateProject()
foundError := false
for _, diag := range v.Diagnostics {
if strings.Contains(diag.Message, "Duplicate Field Definition") && strings.Contains(diag.Message, "FieldX") {
foundError = true
break
}
}
if !foundError {
t.Errorf("Expected duplicate field error for FieldX in +DupNode, but found none")
}
}
func TestMultiFileReference(t *testing.T) {
idx := index.NewProjectTree()
parseAndAddToIndex(t, idx, "integration/multifile_ref_1.marte")
parseAndAddToIndex(t, idx, "integration/multifile_ref_2.marte")
idx.ResolveReferences()
// Check if the reference in +SourceNode to TargetNode is resolved.
v := validator.NewValidator(idx, ".")
v.ValidateProject()
if len(v.Diagnostics) > 0 {
// Filter out irrelevant errors
}
}
func TestHierarchicalPackageMerge(t *testing.T) {
idx := index.NewProjectTree()
parseAndAddToIndex(t, idx, "integration/hierarchical_pkg_1.marte")
parseAndAddToIndex(t, idx, "integration/hierarchical_pkg_2.marte")
v := validator.NewValidator(idx, ".")
v.ValidateProject()
// +MyObj should have Class (from file 1) and FieldX (from file 2).
// If Class is missing, ValidateProject reports error.
for _, diag := range v.Diagnostics {
if strings.Contains(diag.Message, "must contain a 'Class' field") {
t.Errorf("Unexpected 'Class' field error for +MyObj: %s", diag.Message)
}
}
// We can also inspect the tree to verify FieldX is there (optional, but good for confidence)
baseNode := idx.Root.Children["Base"]
if baseNode == nil {
t.Fatal("Base node not found")
}
objNode := baseNode.Children["MyObj"]
if objNode == nil {
t.Fatal("MyObj node not found in Base")
}
hasFieldX := false
for _, frag := range objNode.Fragments {
for _, def := range frag.Definitions {
if f, ok := def.(*parser.Field); ok && f.Name == "FieldX" {
hasFieldX = true
}
}
}
if !hasFieldX {
t.Error("FieldX not found in +MyObj")
}
}
func TestHierarchicalDuplicate(t *testing.T) {
idx := index.NewProjectTree()
parseAndAddToIndex(t, idx, "integration/hierarchical_dup_1.marte")
parseAndAddToIndex(t, idx, "integration/hierarchical_dup_2.marte")
v := validator.NewValidator(idx, ".")
v.ValidateProject()
foundError := false
for _, diag := range v.Diagnostics {
if strings.Contains(diag.Message, "Duplicate Field Definition") && strings.Contains(diag.Message, "FieldY") {
foundError = true
break
}
}
if !foundError {
t.Errorf("Expected duplicate field error for FieldY in +DupObj (hierarchical), but found none")
}
}
func TestIsolatedFileValidation(t *testing.T) {
idx := index.NewProjectTree()
// File 1: Has package. Defines SharedClass.
f1Content := `
#package Proj.Pkg
+SharedObj = { Class = SharedClass }
`
p1 := parser.NewParser(f1Content)
c1, _ := p1.Parse()
idx.AddFile("shared.marte", c1)
// File 2: No package. References SharedObj.
// Should NOT resolve to SharedObj in shared.marte because iso.marte is isolated.
f2Content := `
+IsoObj = {
Class = "MyClass"
Ref = SharedObj
}
`
p2 := parser.NewParser(f2Content)
c2, _ := p2.Parse()
idx.AddFile("iso.marte", c2)
idx.ResolveReferences()
// Find reference
var ref *index.Reference
for i := range idx.References {
if idx.References[i].File == "iso.marte" && idx.References[i].Name == "SharedObj" {
ref = &idx.References[i]
break
}
}
if ref == nil {
t.Fatal("Reference SharedObj not found in index")
}
if ref.Target != nil {
t.Errorf("Expected reference in isolated file to be unresolved, but got target in %s", ref.Target.Fragments[0].File)
}
}