Basic refactoring - towards shared e2e tests

This commit is contained in:
Stanisław Barzowski 2017-12-09 15:41:40 +01:00 committed by Dave Cunningham
parent 8ade994928
commit c3551f4f61
2 changed files with 220 additions and 173 deletions

118
jsonnet_test.go Normal file
View 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)
}
}

View File

@ -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)
}
}