diff --git a/errgoengine.go b/errgoengine.go index cdcfaf1..8270a5f 100644 --- a/errgoengine.go +++ b/errgoengine.go @@ -64,7 +64,7 @@ func (e *ErrgoEngine) Analyze(workingPath, msg string) (*CompiledErrorTemplate, contextData.TraceStack = template.ExtractStackTrace(contextData) // open contents of the extracted stack file locations - if err := ParseFromStackTrace(contextData, template, e.FS); err != nil { + if err := ParseFromStackTrace(contextData, template.Language, e.FS); err != nil { // return error template for bugbuddy to handle // incomplete error messages return template, nil, err @@ -131,12 +131,14 @@ func (e *ErrgoEngine) Translate(template *CompiledErrorTemplate, contextData *Co return expGen.mainExp.String(), output } -func ParseFromStackTrace(contextData *ContextData, template *CompiledErrorTemplate, files *MultiReadFileFS) error { +func ParseFromStackTrace(contextData *ContextData, defaultLanguage *Language, files fs.ReadFileFS) error { parser := sitter.NewParser() analyzer := &SymbolAnalyzer{ContextData: contextData} for _, node := range contextData.TraceStack { - contents, err := files.ReadFile(node.DocumentPath) + path := node.DocumentPath + + contents, err := files.ReadFile(path) if err != nil { // return err // Do not return error if file not found @@ -148,16 +150,16 @@ func ParseFromStackTrace(contextData *ContextData, template *CompiledErrorTempla continue } - var selectedLanguage *Language - existingDoc, docExists := contextData.Documents[node.DocumentPath] + // check if document already exists + existingDoc, docExists := contextData.Documents[path] // check matched languages + selectedLanguage := defaultLanguage if docExists { selectedLanguage = existingDoc.Language } else { - selectedLanguage = template.Language - if !selectedLanguage.MatchPath(node.DocumentPath) { - return fmt.Errorf("no language found for %s", node.DocumentPath) + if !selectedLanguage.MatchPath(path) { + return fmt.Errorf("no language found for %s", path) } // compile language first (if not yet) @@ -165,7 +167,8 @@ func ParseFromStackTrace(contextData *ContextData, template *CompiledErrorTempla } // do semantic analysis - doc, err := ParseDocument(node.DocumentPath, bytes.NewReader(contents), parser, selectedLanguage, existingDoc) + contentReader := bytes.NewReader(contents) + doc, err := ParseDocument(path, contentReader, parser, selectedLanguage, existingDoc) if err != nil { return err } diff --git a/errgoengine_test.go b/errgoengine_test.go new file mode 100644 index 0000000..6a94a0c --- /dev/null +++ b/errgoengine_test.go @@ -0,0 +1,259 @@ +package errgoengine_test + +import ( + "testing" + "testing/fstest" + + lib "github.com/nedpals/errgoengine" + "github.com/nedpals/errgoengine/languages/java" + "github.com/nedpals/errgoengine/languages/python" +) + +func Setup(lang *lib.Language, workingPath string, targetPos lib.Position) *lib.ContextData { + // setup context data + contextData := lib.NewContextData(lib.NewEmptyStore(), ".") + contextData.Analyzer = lang.AnalyzerFactory(contextData) + contextData.TraceStack = lib.TraceStack{} + + // add dummy stack trace item + contextData.TraceStack.Add("main", lib.Location{ + DocumentPath: workingPath, + StartPos: targetPos, + EndPos: targetPos, + }) + + return contextData +} + +func TestParseFromStackTrace(t *testing.T) { + t.Run("Simple/Java", func(t *testing.T) { + currentLang := java.Language + contextData := Setup(currentLang, "Main.java", lib.Position{Line: 4}) + files := fstest.MapFS{ + "Main.java": &fstest.MapFile{ + Data: []byte(`public class Main { + public static void main(String[] args) { + int a = 1; + System.out.println(a/0); + } +}`), + }, + } + + err := lib.ParseFromStackTrace(contextData, currentLang, files) + if err != nil { + t.Fatal(err) + } + + // check if the document is parsed + doc, ok := contextData.Documents["Main.java"] + if !ok { + t.Error("Main.java document not found") + } + + // check if doc language is same as currentLang + if doc.Language == nil { + t.Error("Language is nil") + } + + if doc.Language != currentLang { + t.Errorf("expected language %s, got %s", currentLang.Name, doc.Language.Name) + } + + // check if tree is present + if doc.Tree == nil { + t.Error("Tree is nil") + } + + // check if the content is also present + if doc.Contents == "" { + t.Error("Content is empty") + } + + // check if the tree is parsed + if doc.Tree.RootNode() == nil { + t.Error("Tree is not parsed") + } + }) + + t.Run("Simple/Python", func(t *testing.T) { + currentLang := python.Language + contextData := Setup(currentLang, "main.py", lib.Position{Line: 2}) + files := fstest.MapFS{ + "main.py": &fstest.MapFile{ + Data: []byte(`def main(): + a = 1 + print(a/0) +`), + }, + } + + err := lib.ParseFromStackTrace(contextData, currentLang, files) + if err != nil { + t.Fatal(err) + } + + // check if the document is parsed + doc, ok := contextData.Documents["main.py"] + if !ok { + t.Error("main.py document not found") + } + + // check if doc language is same as currentLang + if doc.Language == nil { + t.Error("Language is nil") + } + + if doc.Language != currentLang { + t.Errorf("expected language %s, got %s", currentLang.Name, doc.Language.Name) + } + + // check if tree is present + if doc.Tree == nil { + t.Error("Tree is nil") + } + + // check if the content is also present + if doc.Contents == "" { + t.Error("Content is empty") + } + + // check if the tree is parsed + if doc.Tree.RootNode() == nil { + t.Error("Tree is not parsed") + } + }) + + t.Run("FileNotFound", func(t *testing.T) { + currentLang := python.Language + contextData := Setup(currentLang, "main.py", lib.Position{Line: 2}) + files := fstest.MapFS{} + + err := lib.ParseFromStackTrace(contextData, currentLang, files) + if err != nil { + t.Fatal(err) + } + + // document should not be present + if _, ok := contextData.Documents["main.py"]; ok { + t.Error("main.py document is present") + } + }) + + t.Run("LanguageNotMatched", func(t *testing.T) { + currentLang := python.Language + contextData := Setup(currentLang, "main.java", lib.Position{Line: 2}) + files := fstest.MapFS{ + "main.java": &fstest.MapFile{ + Data: []byte(`public class Main { + public static void main(String[] args) { + int a = 1; + System.out.println(a/0); + } +}`), + }, + } + + err := lib.ParseFromStackTrace(contextData, currentLang, files) + if err == nil { + t.Error("expected error, got nil") + } + + if err.Error() != "no language found for main.java" { + t.Errorf("expected error message 'no language found for main.java', got %s", err.Error()) + } + + // check if the document is not parsed + if _, ok := contextData.Documents["main.java"]; ok { + t.Error("main.java document is parsed") + } + }) + + t.Run("ExistingDoc", func(t *testing.T) { + currentLang := python.Language + contextData := Setup(currentLang, "main.py", lib.Position{Line: 2}) + files := fstest.MapFS{ + "main.py": &fstest.MapFile{ + Data: []byte(`def main(): + a = 1 + print(a/0) +`), + }, + } + + // parse the document first + err := lib.ParseFromStackTrace(contextData, currentLang, files) + if err != nil { + t.Fatal(err) + } + + // check if the document is parsed + doc, ok := contextData.Documents["main.py"] + if !ok { + t.Error("main.py document not found") + } + + // check if doc language is same as currentLang + if doc.Language == nil { + t.Error("Language is nil") + } + + if doc.Language != currentLang { + t.Errorf("expected language %s, got %s", currentLang.Name, doc.Language.Name) + } + + // check if tree is present + if doc.Tree == nil { + t.Error("Tree is nil") + } + + // check if the content is also present + if doc.Contents == "" { + t.Error("Content is empty") + } + + newFiles := fstest.MapFS{ + "main.py": &fstest.MapFile{ + Data: []byte(`def main(): + print("Hello, World!") +`), + + // change the file modification time + Mode: 0, + }, + } + + oldContent := doc.Contents + + // parse the document again + err = lib.ParseFromStackTrace(contextData, currentLang, newFiles) + if err != nil { + t.Fatal(err) + } + + // check if the document is parsed + doc, ok = contextData.Documents["main.py"] + if !ok { + t.Error("main.py document not found") + } + + // check if doc language is same as currentLang + if doc.Language == nil { + t.Error("Language is nil") + } + + if doc.Language != currentLang { + t.Errorf("expected language %s, got %s", currentLang.Name, doc.Language.Name) + } + + // check if the content is also present + if doc.Contents == "" { + t.Error("Content is empty") + } + + // check if the content is changed + if doc.Contents == oldContent { + t.Error("Content is not changed") + } + }) +}