2022-11-03 13:54:49 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
_ "embed"
|
|
|
|
|
"flag"
|
|
|
|
|
"fmt"
|
2023-01-28 06:20:10 +00:00
|
|
|
stdlog "log"
|
2022-11-03 13:54:49 +00:00
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"regexp"
|
|
|
|
|
"strings"
|
|
|
|
|
"text/template"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"oss.terrastruct.com/d2/lib/log"
|
2023-06-14 18:47:43 +00:00
|
|
|
timelib "oss.terrastruct.com/d2/lib/time"
|
2022-11-03 13:54:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
//go:embed template.html
|
|
|
|
|
var TEMPLATE_HTML string
|
|
|
|
|
|
|
|
|
|
type TemplateData struct {
|
|
|
|
|
Tests []TestItem
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type TestItem struct {
|
|
|
|
|
Name string
|
|
|
|
|
ExpSVG *string
|
|
|
|
|
GotSVG string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
deltaFlag := false
|
|
|
|
|
vFlag := false
|
|
|
|
|
testCaseFlag := ""
|
|
|
|
|
testSetFlag := ""
|
2024-02-23 20:12:42 +00:00
|
|
|
testNameFlag := ""
|
2023-04-28 18:25:22 +00:00
|
|
|
cpuProfileFlag := false
|
|
|
|
|
memProfileFlag := false
|
2022-11-03 13:54:49 +00:00
|
|
|
flag.BoolVar(&deltaFlag, "delta", false, "Generate the report only for cases that changed.")
|
2024-02-23 20:12:42 +00:00
|
|
|
flag.StringVar(&testNameFlag, "test-name", "E2E", "Name of e2e tests. Defaults to E2E")
|
2022-11-03 13:54:49 +00:00
|
|
|
flag.StringVar(&testSetFlag, "test-set", "", "Only run set of tests matching this string. e.g. regressions")
|
|
|
|
|
flag.StringVar(&testCaseFlag, "test-case", "", "Only run tests matching this string. e.g. all_shapes")
|
2023-04-28 18:25:22 +00:00
|
|
|
flag.BoolVar(&cpuProfileFlag, "cpuprofile", false, "Profile test cpu usage. `go tool pprof out/cpu.prof`")
|
|
|
|
|
flag.BoolVar(&memProfileFlag, "memprofile", false, "Profile test memory usage. `go tool pprof out/mem.prof`")
|
2023-02-02 18:48:28 +00:00
|
|
|
skipTests := flag.Bool("skip-tests", false, "Skip running tests first")
|
2022-11-03 13:54:49 +00:00
|
|
|
flag.BoolVar(&vFlag, "v", false, "verbose")
|
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
|
|
vString := ""
|
|
|
|
|
if vFlag {
|
|
|
|
|
vString = "-v"
|
|
|
|
|
}
|
2024-02-23 20:12:42 +00:00
|
|
|
testMatchString := fmt.Sprintf("-run=Test%s/%s/%s", testNameFlag, testSetFlag, testCaseFlag)
|
2023-04-28 18:25:22 +00:00
|
|
|
|
|
|
|
|
cpuProfileStr := ""
|
|
|
|
|
if cpuProfileFlag {
|
|
|
|
|
cpuProfileStr = `-cpuprofile=out/cpu.prof`
|
|
|
|
|
}
|
|
|
|
|
memProfileStr := ""
|
|
|
|
|
if memProfileFlag {
|
|
|
|
|
memProfileStr = `-memprofile=out/mem.prof`
|
|
|
|
|
}
|
2022-11-03 13:54:49 +00:00
|
|
|
|
|
|
|
|
testDir := os.Getenv("TEST_DIR")
|
|
|
|
|
if testDir == "" {
|
|
|
|
|
testDir = "./e2etests"
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-02 18:48:28 +00:00
|
|
|
if !*skipTests {
|
2024-10-03 21:14:14 +00:00
|
|
|
ctx := context.Background()
|
2023-06-09 23:44:10 +00:00
|
|
|
|
2023-06-14 18:47:43 +00:00
|
|
|
ctx, cancel := timelib.WithTimeout(ctx, 2*time.Minute)
|
2023-06-14 00:34:47 +00:00
|
|
|
defer cancel()
|
2023-06-09 23:44:10 +00:00
|
|
|
|
2023-04-29 03:27:06 +00:00
|
|
|
// don't want to pass empty args to CommandContext
|
|
|
|
|
args := []string{"test", testDir, testMatchString}
|
|
|
|
|
if cpuProfileStr != "" {
|
|
|
|
|
args = append(args, cpuProfileStr)
|
|
|
|
|
}
|
|
|
|
|
if memProfileStr != "" {
|
|
|
|
|
args = append(args, memProfileStr)
|
|
|
|
|
}
|
|
|
|
|
if vString != "" {
|
|
|
|
|
args = append(args, vString)
|
|
|
|
|
}
|
|
|
|
|
cmd := exec.CommandContext(ctx, "go", args...)
|
2023-02-02 18:48:28 +00:00
|
|
|
cmd.Env = os.Environ()
|
|
|
|
|
cmd.Env = append(cmd.Env, "FORCE_COLOR=1")
|
|
|
|
|
cmd.Env = append(cmd.Env, "DEBUG=1")
|
|
|
|
|
cmd.Env = append(cmd.Env, "TEST_MODE=on")
|
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
log.Debug(ctx, cmd.String())
|
|
|
|
|
_ = cmd.Run()
|
|
|
|
|
}
|
2022-11-03 13:54:49 +00:00
|
|
|
|
|
|
|
|
var tests []TestItem
|
|
|
|
|
err := filepath.Walk(filepath.Join(testDir, "testdata"), func(path string, info os.FileInfo, err error) error {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if info.IsDir() {
|
2025-02-04 14:24:55 +00:00
|
|
|
files, err := os.ReadDir(path)
|
2022-11-03 13:54:49 +00:00
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var testFile os.FileInfo
|
|
|
|
|
for _, f := range files {
|
|
|
|
|
if strings.HasSuffix(f.Name(), "exp.svg") {
|
2025-02-04 14:24:55 +00:00
|
|
|
testFile, _ = f.Info()
|
2022-11-03 13:54:49 +00:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if testFile != nil {
|
2022-11-24 03:47:05 +00:00
|
|
|
testCaseRoot := filepath.Dir(path)
|
2022-11-03 13:54:49 +00:00
|
|
|
matchTestCase := true
|
|
|
|
|
if testCaseFlag != "" {
|
2022-11-24 03:47:05 +00:00
|
|
|
matchTestCase, _ = regexp.MatchString(testCaseFlag, filepath.Base(testCaseRoot))
|
2022-11-03 13:54:49 +00:00
|
|
|
}
|
|
|
|
|
matchTestSet := true
|
|
|
|
|
if testSetFlag != "" {
|
2022-11-24 03:47:05 +00:00
|
|
|
matchTestSet, _ = regexp.MatchString(testSetFlag, filepath.Base(filepath.Dir(testCaseRoot)))
|
2022-11-03 13:54:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if matchTestSet && matchTestCase {
|
2023-02-10 21:35:28 +00:00
|
|
|
absPath, err := filepath.Abs(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
stdlog.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
fullPath := filepath.Join(absPath, testFile.Name())
|
2022-11-03 13:54:49 +00:00
|
|
|
hasGot := false
|
|
|
|
|
gotPath := strings.Replace(fullPath, "exp.svg", "got.svg", 1)
|
|
|
|
|
if _, err := os.Stat(gotPath); err == nil {
|
|
|
|
|
hasGot = true
|
|
|
|
|
}
|
2022-11-24 04:30:48 +00:00
|
|
|
// e.g. arrowhead_adjustment/dagre
|
|
|
|
|
name := filepath.Join(filepath.Base(testCaseRoot), info.Name())
|
2022-11-03 13:54:49 +00:00
|
|
|
if deltaFlag {
|
|
|
|
|
if hasGot {
|
|
|
|
|
tests = append(tests, TestItem{
|
2022-11-24 04:30:48 +00:00
|
|
|
Name: name,
|
2022-11-03 13:54:49 +00:00
|
|
|
ExpSVG: &fullPath,
|
|
|
|
|
GotSVG: gotPath,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
test := TestItem{
|
2022-11-24 04:30:48 +00:00
|
|
|
Name: name,
|
2022-11-03 13:54:49 +00:00
|
|
|
ExpSVG: nil,
|
|
|
|
|
GotSVG: fullPath,
|
|
|
|
|
}
|
|
|
|
|
if hasGot {
|
|
|
|
|
test.GotSVG = gotPath
|
|
|
|
|
}
|
|
|
|
|
tests = append(tests, test)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(tests) > 0 {
|
|
|
|
|
tmpl, err := template.New("report").Parse(TEMPLATE_HTML)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-04 20:01:35 +00:00
|
|
|
path := os.Getenv("REPORT_OUTPUT")
|
2023-01-28 06:20:10 +00:00
|
|
|
if path == "" {
|
|
|
|
|
path = filepath.Join(testDir, "./out/e2e_report.html")
|
|
|
|
|
}
|
|
|
|
|
err = os.MkdirAll(filepath.Dir(path), 0755)
|
|
|
|
|
if err != nil {
|
|
|
|
|
stdlog.Fatal(err)
|
|
|
|
|
}
|
2022-11-03 13:54:49 +00:00
|
|
|
f, err := os.Create(path)
|
|
|
|
|
if err != nil {
|
2022-11-04 20:04:26 +00:00
|
|
|
panic(fmt.Errorf("error creating file `%s`. %v", path, err))
|
2022-11-03 13:54:49 +00:00
|
|
|
}
|
2023-02-10 21:35:28 +00:00
|
|
|
absReportDir, err := filepath.Abs(filepath.Dir(path))
|
|
|
|
|
if err != nil {
|
|
|
|
|
stdlog.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get the test path relative to the report
|
|
|
|
|
reportRelPath := func(testPath string) string {
|
2023-02-10 21:42:30 +00:00
|
|
|
relTestPath, err := filepath.Rel(absReportDir, testPath)
|
2023-02-10 21:35:28 +00:00
|
|
|
if err != nil {
|
|
|
|
|
stdlog.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
return relTestPath
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// update test paths to be relative to report file
|
|
|
|
|
for i := range tests {
|
|
|
|
|
testItem := &tests[i]
|
|
|
|
|
testItem.GotSVG = reportRelPath(testItem.GotSVG)
|
|
|
|
|
if testItem.ExpSVG != nil {
|
|
|
|
|
*testItem.ExpSVG = reportRelPath(*testItem.ExpSVG)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := tmpl.Execute(f, TemplateData{Tests: tests}); err != nil {
|
2022-11-03 13:54:49 +00:00
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|