mirror of
https://github.com/google/go-jsonnet.git
synced 2025-09-28 17:01:02 +02:00
Basic refactoring - towards shared e2e tests
This commit is contained in:
parent
8ade994928
commit
c3551f4f61
118
jsonnet_test.go
Normal file
118
jsonnet_test.go
Normal file
@ -0,0 +1,118 @@
|
||||
package jsonnet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type errorFormattingTest struct {
|
||||
name string
|
||||
input string
|
||||
errString string
|
||||
}
|
||||
|
||||
func genericTestErrorMessage(t *testing.T, tests []errorFormattingTest, format func(RuntimeError) string) {
|
||||
for _, test := range tests {
|
||||
vm := MakeVM()
|
||||
rawOutput, err := vm.evaluateSnippet(test.name, test.input, evalKindRegular)
|
||||
var errString string
|
||||
if err != nil {
|
||||
switch typedErr := err.(type) {
|
||||
case RuntimeError:
|
||||
errString = format(typedErr)
|
||||
default:
|
||||
t.Errorf("%s: unexpected error: %v", test.name, err)
|
||||
}
|
||||
}
|
||||
output := rawOutput.(string)
|
||||
if errString != test.errString {
|
||||
t.Errorf("%s: error result does not match. got\n\t%+#v\nexpected\n\t%+#v",
|
||||
test.name, errString, test.errString)
|
||||
}
|
||||
if err == nil {
|
||||
t.Errorf("%s, Expected error, but execution succeded and the here's the result:\n %v\n", test.name, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(sbarzowski) Perhaps we should have just one set of tests with all the variants?
|
||||
// TODO(sbarzowski) Perhaps this should be handled in external tests?
|
||||
var oneLineTests = []errorFormattingTest{
|
||||
{"error", `error "x"`, "RUNTIME ERROR: x"},
|
||||
}
|
||||
|
||||
func TestOneLineError(t *testing.T) {
|
||||
genericTestErrorMessage(t, oneLineTests, func(r RuntimeError) string {
|
||||
return r.Error()
|
||||
})
|
||||
}
|
||||
|
||||
// TODO(sbarzowski) checking if the whitespace is right is quite unpleasant, what can we do about it?
|
||||
var minimalErrorTests = []errorFormattingTest{
|
||||
{"error", `error "x"`, "RUNTIME ERROR: x\n" +
|
||||
" error:1:1-10 $\n" + // TODO(sbarzowski) if seems we have off-by-one in location
|
||||
" During evaluation \n" +
|
||||
""},
|
||||
{"error_in_func", `local x(n) = if n == 0 then error "x" else x(n - 1); x(3)`, "RUNTIME ERROR: x\n" +
|
||||
" error_in_func:1:29-38 function <x>\n" +
|
||||
" error_in_func:1:44-52 function <x>\n" +
|
||||
" error_in_func:1:44-52 function <x>\n" +
|
||||
" error_in_func:1:44-52 function <x>\n" +
|
||||
" error_in_func:1:54-58 $\n" +
|
||||
" During evaluation \n" +
|
||||
""},
|
||||
{"error_in_error", `error (error "x")`, "RUNTIME ERROR: x\n" +
|
||||
" error_in_error:1:8-17 $\n" +
|
||||
" During evaluation \n" +
|
||||
""},
|
||||
}
|
||||
|
||||
func TestMinimalError(t *testing.T) {
|
||||
formatter := termErrorFormatter{maxStackTraceSize: 20}
|
||||
genericTestErrorMessage(t, minimalErrorTests, func(r RuntimeError) string {
|
||||
return formatter.Format(r)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO(sbarzowski) test pretty errors once they are stable-ish
|
||||
// probably "golden" pattern is the right one for that
|
||||
|
||||
func removeExcessiveWhitespace(s string) string {
|
||||
var buf bytes.Buffer
|
||||
separated := true
|
||||
for i, w := 0, 0; i < len(s); i += w {
|
||||
runeValue, width := utf8.DecodeRuneInString(s[i:])
|
||||
if runeValue == '\n' || runeValue == ' ' {
|
||||
if !separated {
|
||||
buf.WriteString(" ")
|
||||
separated = true
|
||||
}
|
||||
} else {
|
||||
buf.WriteRune(runeValue)
|
||||
separated = false
|
||||
}
|
||||
w = width
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func TestCustomImporter(t *testing.T) {
|
||||
vm := MakeVM()
|
||||
vm.Importer(&MemoryImporter{
|
||||
map[string]string{
|
||||
"a.jsonnet": "2 + 2",
|
||||
"b.jsonnet": "3 + 3",
|
||||
},
|
||||
})
|
||||
input := `[import "a.jsonnet", importstr "b.jsonnet"]`
|
||||
expected := `[ 4, "3 + 3" ] `
|
||||
actual, err := vm.EvaluateSnippet("custom_import.jsonnet", input)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
actual = removeExcessiveWhitespace(actual)
|
||||
if actual != expected {
|
||||
t.Errorf("Expected %q, but got %q", expected, actual)
|
||||
}
|
||||
}
|
275
main_test.go
275
main_test.go
@ -24,7 +24,6 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/google/go-jsonnet/ast"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
@ -70,6 +69,106 @@ type mainTest struct {
|
||||
meta *testMetadata
|
||||
}
|
||||
|
||||
var jsonToString = &NativeFunction{
|
||||
Name: "jsonToString",
|
||||
Params: ast.Identifiers{"x"},
|
||||
Func: func(x []interface{}) (interface{}, error) {
|
||||
bytes, err := json.Marshal(x[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(bytes), nil
|
||||
},
|
||||
}
|
||||
|
||||
type jsonnetInput struct {
|
||||
name string
|
||||
input []byte
|
||||
eKind evalKind
|
||||
extVars map[string]string
|
||||
extCode map[string]string
|
||||
}
|
||||
|
||||
type jsonnetResult struct {
|
||||
output string
|
||||
isError bool
|
||||
}
|
||||
|
||||
func runJsonnet(i jsonnetInput) jsonnetResult {
|
||||
vm := MakeVM()
|
||||
errFormatter := termErrorFormatter{pretty: true, maxStackTraceSize: 9}
|
||||
|
||||
for name, value := range i.extVars {
|
||||
vm.ExtVar(name, value)
|
||||
}
|
||||
for name, value := range i.extCode {
|
||||
vm.ExtCode(name, value)
|
||||
}
|
||||
|
||||
vm.NativeFunction(jsonToString)
|
||||
|
||||
var output string
|
||||
|
||||
rawOutput, err := vm.evaluateSnippet(i.name, string(i.input), i.eKind)
|
||||
var isError bool
|
||||
if err != nil {
|
||||
// TODO(sbarzowski) perhaps somehow mark that we are processing
|
||||
// an error. But for now we can treat them the same.
|
||||
output = errFormatter.Format(err)
|
||||
output += "\n"
|
||||
isError = true
|
||||
} else {
|
||||
output = rawOutput.(string)
|
||||
isError = false
|
||||
}
|
||||
|
||||
return jsonnetResult{
|
||||
output: output,
|
||||
isError: isError,
|
||||
}
|
||||
}
|
||||
|
||||
func runTest(t *testing.T, test *mainTest) {
|
||||
|
||||
read := func(file string) []byte {
|
||||
bytz, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatalf("reading file: %s: %v", file, err)
|
||||
}
|
||||
return bytz
|
||||
}
|
||||
|
||||
input := read(test.input)
|
||||
|
||||
result := runJsonnet(jsonnetInput{
|
||||
name: test.name,
|
||||
input: input,
|
||||
eKind: evalKindRegular,
|
||||
extVars: test.meta.extVars,
|
||||
extCode: test.meta.extCode,
|
||||
})
|
||||
|
||||
// TODO(sbarzowski) report which files were updated
|
||||
if *update {
|
||||
err := ioutil.WriteFile(test.golden, []byte(result.output), 0666)
|
||||
if err != nil {
|
||||
t.Errorf("error updating golden files: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
golden := read(test.golden)
|
||||
if bytes.Compare(golden, []byte(result.output)) != 0 {
|
||||
// TODO(sbarzowski) better reporting of differences in whitespace
|
||||
// missing newline issues can be very subtle now
|
||||
t.Fail()
|
||||
t.Errorf("Mismatch when running %s.jsonnet. Golden: %s\n", test.name, test.golden)
|
||||
data := diff(result.output, string(golden))
|
||||
t.Errorf("diff %s jsonnet %s.jsonnet\n", test.golden, test.name)
|
||||
t.Errorf(string(data))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
flag.Parse()
|
||||
var mainTests []mainTest
|
||||
@ -77,6 +176,7 @@ func TestMain(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, input := range match {
|
||||
golden := input
|
||||
name := input
|
||||
@ -90,69 +190,9 @@ func TestMain(t *testing.T) {
|
||||
}
|
||||
mainTests = append(mainTests, mainTest{name: name, input: input, golden: golden, meta: &meta})
|
||||
}
|
||||
errFormatter := termErrorFormatter{pretty: true, maxStackTraceSize: 9}
|
||||
for _, test := range mainTests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
vm := MakeVM()
|
||||
|
||||
for name, value := range test.meta.extVars {
|
||||
vm.ExtVar(name, value)
|
||||
}
|
||||
for name, value := range test.meta.extCode {
|
||||
vm.ExtCode(name, value)
|
||||
}
|
||||
|
||||
vm.NativeFunction(&NativeFunction{
|
||||
Name: "jsonToString",
|
||||
Params: ast.Identifiers{"x"},
|
||||
Func: func(x []interface{}) (interface{}, error) {
|
||||
bytes, err := json.Marshal(x[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(bytes), nil
|
||||
},
|
||||
})
|
||||
read := func(file string) []byte {
|
||||
bytz, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatalf("reading file: %s: %v", file, err)
|
||||
}
|
||||
return bytz
|
||||
}
|
||||
|
||||
input := read(test.input)
|
||||
rawOutput, err := vm.evaluateSnippet(test.name, string(input), evalKindRegular)
|
||||
var output string
|
||||
if err != nil {
|
||||
// TODO(sbarzowski) perhaps somehow mark that we are processing
|
||||
// an error. But for now we can treat them the same.
|
||||
output = errFormatter.Format(err)
|
||||
output += "\n"
|
||||
} else {
|
||||
output = rawOutput.(string)
|
||||
}
|
||||
if *update {
|
||||
err := ioutil.WriteFile(test.golden, []byte(output), 0666)
|
||||
if err != nil {
|
||||
t.Errorf("error updating golden files: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
golden := read(test.golden)
|
||||
if bytes.Compare(golden, []byte(output)) != 0 {
|
||||
// TODO(sbarzowski) better reporting of differences in whitespace
|
||||
// missing newline issues can be very subtle now
|
||||
t.Fail()
|
||||
t.Errorf("Mismatch when running %s.jsonnet. Golden: %s\n", test.name, test.golden)
|
||||
data := diff(output, string(golden))
|
||||
if err != nil {
|
||||
t.Errorf("computing diff: %s", err)
|
||||
}
|
||||
t.Errorf("diff %s jsonnet %s.jsonnet\n", test.golden, test.name)
|
||||
t.Errorf(string(data))
|
||||
|
||||
}
|
||||
runTest(t, &test)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -162,114 +202,3 @@ func diff(a, b string) string {
|
||||
diffs := dmp.DiffMain(a, b, false)
|
||||
return dmp.DiffPrettyText(diffs)
|
||||
}
|
||||
|
||||
type errorFormattingTest struct {
|
||||
name string
|
||||
input string
|
||||
errString string
|
||||
}
|
||||
|
||||
func genericTestErrorMessage(t *testing.T, tests []errorFormattingTest, format func(RuntimeError) string) {
|
||||
for _, test := range tests {
|
||||
vm := MakeVM()
|
||||
rawOutput, err := vm.evaluateSnippet(test.name, test.input, evalKindRegular)
|
||||
var errString string
|
||||
if err != nil {
|
||||
switch typedErr := err.(type) {
|
||||
case RuntimeError:
|
||||
errString = format(typedErr)
|
||||
default:
|
||||
t.Errorf("%s: unexpected error: %v", test.name, err)
|
||||
}
|
||||
}
|
||||
output := rawOutput.(string)
|
||||
if errString != test.errString {
|
||||
t.Errorf("%s: error result does not match. got\n\t%+#v\nexpected\n\t%+#v",
|
||||
test.name, errString, test.errString)
|
||||
}
|
||||
if err == nil {
|
||||
t.Errorf("%s, Expected error, but execution succeded and the here's the result:\n %v\n", test.name, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(sbarzowski) Perhaps we should have just one set of tests with all the variants?
|
||||
// TODO(sbarzowski) Perhaps this should be handled in external tests?
|
||||
var oneLineTests = []errorFormattingTest{
|
||||
{"error", `error "x"`, "RUNTIME ERROR: x"},
|
||||
}
|
||||
|
||||
func TestOneLineError(t *testing.T) {
|
||||
genericTestErrorMessage(t, oneLineTests, func(r RuntimeError) string {
|
||||
return r.Error()
|
||||
})
|
||||
}
|
||||
|
||||
// TODO(sbarzowski) checking if the whitespace is right is quite unpleasant, what can we do about it?
|
||||
var minimalErrorTests = []errorFormattingTest{
|
||||
{"error", `error "x"`, "RUNTIME ERROR: x\n" +
|
||||
" error:1:1-10 $\n" + // TODO(sbarzowski) if seems we have off-by-one in location
|
||||
" During evaluation \n" +
|
||||
""},
|
||||
{"error_in_func", `local x(n) = if n == 0 then error "x" else x(n - 1); x(3)`, "RUNTIME ERROR: x\n" +
|
||||
" error_in_func:1:29-38 function <x>\n" +
|
||||
" error_in_func:1:44-52 function <x>\n" +
|
||||
" error_in_func:1:44-52 function <x>\n" +
|
||||
" error_in_func:1:44-52 function <x>\n" +
|
||||
" error_in_func:1:54-58 $\n" +
|
||||
" During evaluation \n" +
|
||||
""},
|
||||
{"error_in_error", `error (error "x")`, "RUNTIME ERROR: x\n" +
|
||||
" error_in_error:1:8-17 $\n" +
|
||||
" During evaluation \n" +
|
||||
""},
|
||||
}
|
||||
|
||||
func TestMinimalError(t *testing.T) {
|
||||
formatter := termErrorFormatter{maxStackTraceSize: 20}
|
||||
genericTestErrorMessage(t, minimalErrorTests, func(r RuntimeError) string {
|
||||
return formatter.Format(r)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO(sbarzowski) test pretty errors once they are stable-ish
|
||||
// probably "golden" pattern is the right one for that
|
||||
|
||||
func removeExcessiveWhitespace(s string) string {
|
||||
var buf bytes.Buffer
|
||||
separated := true
|
||||
for i, w := 0, 0; i < len(s); i += w {
|
||||
runeValue, width := utf8.DecodeRuneInString(s[i:])
|
||||
if runeValue == '\n' || runeValue == ' ' {
|
||||
if !separated {
|
||||
buf.WriteString(" ")
|
||||
separated = true
|
||||
}
|
||||
} else {
|
||||
buf.WriteRune(runeValue)
|
||||
separated = false
|
||||
}
|
||||
w = width
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func TestCustomImporter(t *testing.T) {
|
||||
vm := MakeVM()
|
||||
vm.Importer(&MemoryImporter{
|
||||
map[string]string{
|
||||
"a.jsonnet": "2 + 2",
|
||||
"b.jsonnet": "3 + 3",
|
||||
},
|
||||
})
|
||||
input := `[import "a.jsonnet", importstr "b.jsonnet"]`
|
||||
expected := `[ 4, "3 + 3" ] `
|
||||
actual, err := vm.EvaluateSnippet("custom_import.jsonnet", input)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
actual = removeExcessiveWhitespace(actual)
|
||||
if actual != expected {
|
||||
t.Errorf("Expected %q, but got %q", expected, actual)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user