mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
143 lines
3.6 KiB
Go
143 lines
3.6 KiB
Go
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
|
|
// Licensed under the GNU Affero General Public License (AGPL).
|
|
// See License.AGPL.txt in the project root for license information.
|
|
|
|
package testing
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/go-test/deep"
|
|
"google.golang.org/protobuf/encoding/protojson"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
var update = flag.Bool("update", false, "update .golden files")
|
|
var force = flag.Bool("force", false, "overwrite .golden files even if they already exist")
|
|
|
|
// FixtureTest is a test that is based on fixture and golden files. This is very convenient to test a largely variable surface with many variants.
|
|
type FixtureTest struct {
|
|
T *testing.T
|
|
Path string
|
|
GoldPath func(path string) string
|
|
Test FixtureTestFunc
|
|
Fixture func() interface{}
|
|
Gold func() interface{}
|
|
}
|
|
|
|
// FixtureTestFunc implements the actual fixture test
|
|
type FixtureTestFunc func(t *testing.T, fixture interface{}) interface{}
|
|
|
|
// Run executes the fixture test - do not forget to call this one
|
|
func (ft *FixtureTest) Run() {
|
|
t := ft.T
|
|
|
|
fixtures, err := filepath.Glob(ft.Path)
|
|
if err != nil {
|
|
t.Error("cannot list test fixtures: ", err)
|
|
return
|
|
}
|
|
|
|
for _, fn := range fixtures {
|
|
t.Run(fn, func(t *testing.T) {
|
|
fd, err := os.ReadFile(fn)
|
|
if err != nil {
|
|
t.Errorf("cannot read %s: %v", fn, err)
|
|
return
|
|
}
|
|
|
|
fixture := ft.Fixture()
|
|
if typ := reflect.TypeOf(fixture); typ.Kind() != reflect.Ptr {
|
|
t.Error("Fixture() did not return a pointer")
|
|
return
|
|
}
|
|
if msg, ok := fixture.(proto.Message); ok {
|
|
err = protojson.Unmarshal(fd, msg)
|
|
if err != nil {
|
|
t.Errorf("cannot unmarshal %s: %v", fn, err)
|
|
return
|
|
}
|
|
} else {
|
|
err = json.Unmarshal(fd, fixture)
|
|
if err != nil {
|
|
t.Errorf("cannot unmarshal %s: %v", fn, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
result := ft.Test(t, fixture)
|
|
if result == nil {
|
|
// Test routine is expected to complain using t.Errorf
|
|
t.Logf("test routine for %s returned nil - continuing", fn)
|
|
return
|
|
}
|
|
if typ := reflect.TypeOf(result); typ.Kind() != reflect.Ptr {
|
|
t.Error("Test() did not return a pointer")
|
|
return
|
|
}
|
|
|
|
actual, err := json.MarshalIndent(result, "", " ")
|
|
if err != nil {
|
|
t.Errorf("cannot marshal status for %s: %v", fn, err)
|
|
return
|
|
}
|
|
|
|
goldenFilePath := fmt.Sprintf("%s.golden", strings.TrimSuffix(fn, filepath.Ext(fn)))
|
|
if ft.GoldPath != nil {
|
|
goldenFilePath = ft.GoldPath(fn)
|
|
}
|
|
if *update {
|
|
if _, err := os.Stat(goldenFilePath); *force || errors.Is(err, fs.ErrNotExist) {
|
|
err = os.WriteFile(goldenFilePath, actual, 0600)
|
|
if err != nil {
|
|
t.Errorf("cannot write gold standard %s: %v", goldenFilePath, err)
|
|
return
|
|
}
|
|
|
|
t.Logf("Wrote new gold standard in %s", goldenFilePath)
|
|
} else {
|
|
t.Logf("Did not overwrite gold standard in %s", goldenFilePath)
|
|
}
|
|
}
|
|
|
|
expected, err := os.ReadFile(goldenFilePath)
|
|
if err != nil {
|
|
t.Errorf("cannot read golden file %s: %v", goldenFilePath, err)
|
|
return
|
|
}
|
|
expected = bytes.TrimSpace(expected)
|
|
|
|
if !bytes.Equal(actual, expected) {
|
|
expectedResult := ft.Gold()
|
|
if typ := reflect.TypeOf(expectedResult); typ.Kind() != reflect.Ptr {
|
|
t.Error("Gold() did not return a pointer")
|
|
return
|
|
}
|
|
|
|
err = json.Unmarshal(expected, expectedResult)
|
|
if err != nil {
|
|
t.Errorf("cannot unmarshal JSON %s: %v", goldenFilePath, err)
|
|
return
|
|
}
|
|
|
|
diff := deep.Equal(expectedResult, result)
|
|
if len(diff) > 0 {
|
|
t.Errorf("fixture %s: %v", fn, diff)
|
|
}
|
|
|
|
return
|
|
}
|
|
})
|
|
}
|
|
}
|