1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build ignore
6
7package main
8
9import (
10	"bytes"
11	"fmt"
12	"internal/trace/raw"
13	"internal/trace/version"
14	"internal/txtar"
15	"io"
16	"log"
17	"os"
18	"os/exec"
19	"path/filepath"
20	"regexp"
21)
22
23func main() {
24	log.SetFlags(0)
25	ctx, err := newContext()
26	if err != nil {
27		log.Fatal(err)
28	}
29	if err := ctx.runGenerators(); err != nil {
30		log.Fatal(err)
31	}
32	if err := ctx.runTestProg("./testprog/annotations.go"); err != nil {
33		log.Fatal(err)
34	}
35	if err := ctx.runTestProg("./testprog/annotations-stress.go"); err != nil {
36		log.Fatal(err)
37	}
38}
39
40type context struct {
41	testNames map[string]struct{}
42	filter    *regexp.Regexp
43}
44
45func newContext() (*context, error) {
46	var filter *regexp.Regexp
47	var err error
48	if pattern := os.Getenv("GOTRACETEST"); pattern != "" {
49		filter, err = regexp.Compile(pattern)
50		if err != nil {
51			return nil, fmt.Errorf("compiling regexp %q for GOTRACETEST: %v", pattern, err)
52		}
53	}
54	return &context{
55		testNames: make(map[string]struct{}),
56		filter:    filter,
57	}, nil
58}
59
60func (ctx *context) register(testName string) (skip bool, err error) {
61	if _, ok := ctx.testNames[testName]; ok {
62		return true, fmt.Errorf("duplicate test %s found", testName)
63	}
64	if ctx.filter != nil {
65		return !ctx.filter.MatchString(testName), nil
66	}
67	return false, nil
68}
69
70func (ctx *context) runGenerators() error {
71	generators, err := filepath.Glob("./generators/*.go")
72	if err != nil {
73		return fmt.Errorf("reading generators: %v", err)
74	}
75	genroot := "./tests"
76
77	if err := os.MkdirAll(genroot, 0777); err != nil {
78		return fmt.Errorf("creating generated root: %v", err)
79	}
80	for _, path := range generators {
81		name := filepath.Base(path)
82		name = name[:len(name)-len(filepath.Ext(name))]
83
84		// Skip if we have a pattern and this test doesn't match.
85		skip, err := ctx.register(name)
86		if err != nil {
87			return err
88		}
89		if skip {
90			continue
91		}
92
93		fmt.Fprintf(os.Stderr, "generating %s... ", name)
94
95		// Get the test path.
96		testPath := filepath.Join(genroot, fmt.Sprintf("%s.test", name))
97
98		// Run generator.
99		cmd := exec.Command("go", "run", path, testPath)
100		if out, err := cmd.CombinedOutput(); err != nil {
101			return fmt.Errorf("running generator %s: %v:\n%s", name, err, out)
102		}
103		fmt.Fprintln(os.Stderr)
104	}
105	return nil
106}
107
108func (ctx *context) runTestProg(progPath string) error {
109	name := filepath.Base(progPath)
110	name = name[:len(name)-len(filepath.Ext(name))]
111	name = fmt.Sprintf("go1%d-%s", version.Current, name)
112
113	// Skip if we have a pattern and this test doesn't match.
114	skip, err := ctx.register(name)
115	if err != nil {
116		return err
117	}
118	if skip {
119		return nil
120	}
121
122	// Create command.
123	var trace, stderr bytes.Buffer
124	cmd := exec.Command("go", "run", progPath)
125	cmd.Stdout = &trace
126	cmd.Stderr = &stderr
127
128	// Run trace program; the trace will appear in stdout.
129	fmt.Fprintf(os.Stderr, "running trace program %s...\n", name)
130	if err := cmd.Run(); err != nil {
131		log.Fatalf("running trace program: %v:\n%s", err, stderr.String())
132	}
133
134	// Write out the trace.
135	var textTrace bytes.Buffer
136	r, err := raw.NewReader(&trace)
137	if err != nil {
138		log.Fatalf("reading trace: %v", err)
139	}
140	w, err := raw.NewTextWriter(&textTrace, version.Current)
141	for {
142		ev, err := r.ReadEvent()
143		if err == io.EOF {
144			break
145		}
146		if err != nil {
147			log.Fatalf("reading trace: %v", err)
148		}
149		if err := w.WriteEvent(ev); err != nil {
150			log.Fatalf("writing trace: %v", err)
151		}
152	}
153	testData := txtar.Format(&txtar.Archive{
154		Files: []txtar.File{
155			{Name: "expect", Data: []byte("SUCCESS")},
156			{Name: "trace", Data: textTrace.Bytes()},
157		},
158	})
159	return os.WriteFile(fmt.Sprintf("./tests/%s.test", name), testData, 0o664)
160}
161