1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
package main
import (
"bytes"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/diagnostics"
)
// Test the error messages of the TinyGo compiler.
func TestErrors(t *testing.T) {
for _, name := range []string{
"cgo",
"compiler",
"interp",
"loader-importcycle",
"loader-invaliddep",
"loader-invalidpackage",
"loader-nopackage",
"optimizer",
"syntax",
"types",
} {
t.Run(name, func(t *testing.T) {
testErrorMessages(t, "./testdata/errors/"+name+".go")
})
}
}
func testErrorMessages(t *testing.T, filename string) {
// Parse expected error messages.
expected := readErrorMessages(t, filename)
// Try to build a binary (this should fail with an error).
tmpdir := t.TempDir()
err := Build(filename, tmpdir+"/out", &compileopts.Options{
Target: "wasip1",
Semaphore: sema,
InterpTimeout: 180 * time.Second,
Debug: true,
VerifyIR: true,
Opt: "z",
})
if err == nil {
t.Fatal("expected to get a compiler error")
}
// Get the full ./testdata/errors directory.
wd, absErr := filepath.Abs("testdata/errors")
if absErr != nil {
t.Fatal(absErr)
}
// Write error message out as plain text.
var buf bytes.Buffer
diagnostics.CreateDiagnostics(err).WriteTo(&buf, wd)
actual := strings.TrimRight(buf.String(), "\n")
// Check whether the error is as expected.
if !matchErrors(t, expected, actual) {
t.Errorf("expected error:\n%s\ngot:\n%s", indentText(expected, "> "), indentText(actual, "> "))
}
}
func matchErrors(t *testing.T, pattern, actual string) bool {
patternLines := strings.Split(pattern, "\n")
actualLines := strings.Split(actual, "\n")
if len(patternLines) != len(actualLines) {
return false
}
for i, patternLine := range patternLines {
indices := regexp.MustCompile(`\{\{.*?\}\}`).FindAllStringIndex(patternLine, -1)
patternParts := []string{"^"}
lastStop := 0
for _, startstop := range indices {
start := startstop[0]
stop := startstop[1]
patternParts = append(patternParts,
regexp.QuoteMeta(patternLine[lastStop:start]),
patternLine[start+2:stop-2])
lastStop = stop
}
patternParts = append(patternParts, regexp.QuoteMeta(patternLine[lastStop:]), "$")
pattern := strings.Join(patternParts, "")
re, err := regexp.Compile(pattern)
if err != nil {
t.Fatalf("could not compile regexp for %#v: %v", patternLine, err)
}
if !re.MatchString(actualLines[i]) {
return false
}
}
return true
}
// Indent the given text with a given indentation string.
func indentText(text, indent string) string {
return indent + strings.ReplaceAll(text, "\n", "\n"+indent)
}
// Read "// ERROR:" prefixed messages from the given file.
func readErrorMessages(t *testing.T, file string) string {
data, err := os.ReadFile(file)
if err != nil {
t.Fatal("could not read input file:", err)
}
var errors []string
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "// ERROR: ") {
errors = append(errors, strings.TrimRight(line[len("// ERROR: "):], "\r\n"))
}
}
return strings.Join(errors, "\n")
}
|