aboutsummaryrefslogtreecommitdiff
path: root/compiler_wrapper/goldenutil_test.go
diff options
context:
space:
mode:
authorTobias Bosch <tbosch@google.com>2019-07-15 12:22:50 -0700
committerTobias Bosch <tbosch@google.com>2019-07-18 09:09:26 +0000
commit3d89c0b95331323feafcf12181f44ca26bdeb96d (patch)
treeebbbb6cdae92f550a642aaf9d3c9f4432bb847db /compiler_wrapper/goldenutil_test.go
parent198a3c9519e5d93ffeb8f5e1b6694c34b178c5c4 (diff)
downloadtoolchain-utils-3d89c0b95331323feafcf12181f44ca26bdeb96d.tar.gz
Create golden tests for sysroot wrapper.
- Golden files can be updated via the new "-updategolden" test flag. - Golden files contain the wrapper command, commands executed by the wrapper stdout, stderr, exitcode of the wrapper - The commands in the golden files are also compared to the old wrapper to make sure they are correct. We are also mocking less things in the old wrapper now, allowing to compare against differnet wrappers via golden tests in the future (e.g. clang_host_wrapper). - Unit tests are no longer diffing against the old wrapper. BUG=chromium:773875 TEST=unit test Change-Id: I0755bfe11cd499474820c9354412d39a0fa04401 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/1702633 Tested-by: Tobias Bosch <tbosch@google.com> Reviewed-by: George Burgess <gbiv@chromium.org>
Diffstat (limited to 'compiler_wrapper/goldenutil_test.go')
-rw-r--r--compiler_wrapper/goldenutil_test.go189
1 files changed, 189 insertions, 0 deletions
diff --git a/compiler_wrapper/goldenutil_test.go b/compiler_wrapper/goldenutil_test.go
new file mode 100644
index 00000000..d5d1ec28
--- /dev/null
+++ b/compiler_wrapper/goldenutil_test.go
@@ -0,0 +1,189 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "flag"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+)
+
+var updateGoldenFiles = flag.Bool("updategolden", false, "update golden files")
+var filterGoldenTests = flag.String("rungolden", "", "regex filter for golden tests to run")
+
+type goldenRecordSection struct {
+ Name string `json:"name"`
+ ignoreOldWrapper bool
+ Records []goldenRecord `json:"records"`
+}
+
+type goldenRecord struct {
+ Wd string `json:"wd"`
+ Env []string `json:"env,omitempty"`
+ // runGoldenRecords will read cmd and fill
+ // stdout, stderr, exitCode.
+ WrapperCmd commandResult `json:"wrapper"`
+ // runGoldenRecords will read stdout, stderr, err
+ // and fill cmd
+ Cmds []commandResult `json:"cmds"`
+}
+
+func newGoldenCmd(path string, args ...string) commandResult {
+ return commandResult{
+ Cmd: &command{
+ Path: path,
+ Args: args,
+ },
+ }
+}
+
+var okResult = commandResult{}
+var okResults = []commandResult{okResult}
+var errorResult = commandResult{
+ ExitCode: 1,
+ Stderr: "someerror",
+ Stdout: "somemessage",
+}
+var errorResults = []commandResult{errorResult}
+
+func runGoldenRecords(ctx *testContext, goldenFile string, sections []goldenRecordSection) {
+ if filterPattern := *filterGoldenTests; filterPattern != "" {
+ sections = filterGoldenRecords(filterPattern, sections)
+ }
+ if len(sections) == 0 {
+ ctx.t.Errorf("No goldenrecords given.")
+ return
+ }
+ sections = fillGoldenResults(ctx, sections)
+ if *updateGoldenFiles {
+ log.Printf("updating golden file under %s", goldenFile)
+ if err := os.MkdirAll(filepath.Dir(goldenFile), 0777); err != nil {
+ ctx.t.Fatal(err)
+ }
+ goldenFile, err := os.Create(goldenFile)
+ if err != nil {
+ ctx.t.Fatal(err)
+ }
+ defer goldenFile.Close()
+
+ writeGoldenRecords(ctx, goldenFile, sections)
+ } else {
+ compareBuffer := &bytes.Buffer{}
+ writeGoldenRecords(ctx, compareBuffer, sections)
+ goldenFileData, err := ioutil.ReadFile(goldenFile)
+ if err != nil {
+ ctx.t.Fatal(err)
+ }
+ if !bytes.Equal(compareBuffer.Bytes(), goldenFileData) {
+ ctx.t.Fatalf("Commands don't match the golden file under %s. Please regenerate via -updategolden to check the differences.",
+ goldenFile)
+ }
+ }
+}
+
+func filterGoldenRecords(pattern string, sections []goldenRecordSection) []goldenRecordSection {
+ matcher := regexp.MustCompile(pattern)
+ newSections := []goldenRecordSection{}
+ for _, section := range sections {
+ newRecords := []goldenRecord{}
+ for _, record := range section.Records {
+ cmd := record.WrapperCmd.Cmd
+ str := strings.Join(append(append(record.Env, cmd.Path), cmd.Args...), " ")
+ if matcher.MatchString(str) {
+ newRecords = append(newRecords, record)
+ }
+ }
+ section.Records = newRecords
+ newSections = append(newSections, section)
+ }
+ return newSections
+}
+
+func fillGoldenResults(ctx *testContext, sections []goldenRecordSection) []goldenRecordSection {
+ oldWrapperPath := ctx.cfg.oldWrapperPath
+ newSections := []goldenRecordSection{}
+ for _, section := range sections {
+ ctx.cfg.oldWrapperPath = oldWrapperPath
+ if section.ignoreOldWrapper {
+ ctx.cfg.oldWrapperPath = ""
+ }
+
+ newRecords := []goldenRecord{}
+ for _, record := range section.Records {
+ newCmds := []commandResult{}
+ ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ if len(newCmds) >= len(record.Cmds) {
+ ctx.t.Errorf("Not enough commands specified for wrapperCmd %#v and env %#v. Expected: %#v",
+ record.WrapperCmd.Cmd, record.Env, record.Cmds)
+ return nil
+ }
+ cmdResult := record.Cmds[len(newCmds)]
+ cmdResult.Cmd = cmd
+ newCmds = append(newCmds, cmdResult)
+ io.WriteString(stdout, cmdResult.Stdout)
+ io.WriteString(stderr, cmdResult.Stderr)
+ if cmdResult.ExitCode != 0 {
+ return newExitCodeError(cmdResult.ExitCode)
+ }
+ return nil
+ }
+ ctx.stdoutBuffer.Reset()
+ ctx.stderrBuffer.Reset()
+ ctx.env = record.Env
+ if record.Wd == "" {
+ record.Wd = ctx.tempDir
+ }
+ ctx.wd = record.Wd
+ // Create an empty wrapper at the given path.
+ // Needed as we are resolving symlinks which stats the wrapper file.
+ ctx.writeFile(record.WrapperCmd.Cmd.Path, "")
+ record.WrapperCmd.ExitCode = callCompiler(ctx, ctx.cfg, record.WrapperCmd.Cmd)
+ if hasInternalError(ctx.stderrString()) {
+ ctx.t.Errorf("found an internal error for wrapperCmd %#v and env #%v. Got: %s",
+ record.WrapperCmd.Cmd, record.Env, ctx.stderrString())
+ }
+ if len(newCmds) < len(record.Cmds) {
+ ctx.t.Errorf("Too many commands specified for wrapperCmd %#v and env %#v. Expected: %#v",
+ record.WrapperCmd.Cmd, record.Env, record.Cmds)
+ }
+ record.Cmds = newCmds
+ record.WrapperCmd.Stdout = ctx.stdoutString()
+ record.WrapperCmd.Stderr = ctx.stderrString()
+ newRecords = append(newRecords, record)
+ }
+ section.Records = newRecords
+ newSections = append(newSections, section)
+ }
+ return newSections
+}
+
+func writeGoldenRecords(ctx *testContext, writer io.Writer, sections []goldenRecordSection) {
+ // Replace the temp dir with a stable path so that the goldens stay stable.
+ stableTempDir := filepath.Join(filepath.Dir(ctx.tempDir), "stable")
+ writer = &replacingWriter{
+ Writer: writer,
+ old: []byte(ctx.tempDir),
+ new: []byte(stableTempDir),
+ }
+ enc := json.NewEncoder(writer)
+ enc.SetIndent("", " ")
+ if err := enc.Encode(sections); err != nil {
+ ctx.t.Fatal(err)
+ }
+}
+
+type replacingWriter struct {
+ io.Writer
+ old []byte
+ new []byte
+}
+
+func (writer *replacingWriter) Write(p []byte) (n int, err error) {
+ p = bytes.ReplaceAll(p, writer.old, writer.new)
+ return writer.Writer.Write(p)
+}