/vendor/github.com/smartystreets/assertions/internal/ogletest/integration_test.go
Go | 265 lines | 151 code | 47 blank | 67 comment | 44 complexity | 8e5c32b2ca2ffe866e4282f227893636 MD5 | raw file
- // Copyright 2011 Aaron Jacobs. All Rights Reserved.
- // Author: aaronjjacobs@gmail.com (Aaron Jacobs)
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package ogletest_test
- import (
- "errors"
- "flag"
- "fmt"
- "go/build"
- "io/ioutil"
- "os"
- "os/exec"
- "path"
- "regexp"
- "strings"
- "syscall"
- "testing"
- )
- const ogletestPkg = "github.com/smartystreets/assertions/internal/ogletest"
- var dumpNew = flag.Bool("dump_new", false, "Dump new golden files.")
- var objDir string
- ////////////////////////////////////////////////////////////////////////
- // Helpers
- ////////////////////////////////////////////////////////////////////////
- // Install the possibly locally-modified copy of ogletest, so that these
- // integration tests run using the package currently being worked on by the
- // programmer. Also install other dependencies needed by the test cases, so
- // that `go test` complaining about non-up-to-date packages doesn't make it
- // into the golden files.
- func installLocalPackages() error {
- cmd := exec.Command(
- "go",
- "install",
- ogletestPkg,
- "github.com/smartystreets/assertions/internal/oglemock",
- "github.com/smartystreets/assertions/internal/ogletest/test_cases/mock_image")
- output, err := cmd.CombinedOutput()
- if err != nil {
- return errors.New(fmt.Sprintf("%v:\n%s", err, output))
- }
- return nil
- }
- // getCaseNames looks for integration test cases as files in the test_cases
- // directory.
- func getCaseNames() ([]string, error) {
- // Open the test cases directory.
- dir, err := os.Open("test_cases")
- if err != nil {
- return nil, errors.New(fmt.Sprintf("Opening dir: %v", err))
- }
- // Get a list of the names in the directory.
- names, err := dir.Readdirnames(0)
- if err != nil {
- return nil, errors.New(fmt.Sprintf("Readdirnames: %v", err))
- }
- // Filter the names.
- result := make([]string, len(names))
- resultLen := 0
- for _, name := range names {
- // Skip golden files and hidden files.
- if strings.HasPrefix(name, "golden.") || strings.HasPrefix(name, ".") {
- continue
- }
- // Check for the right format.
- if !strings.HasSuffix(name, ".test.go") {
- continue
- }
- // Store the name minus the extension.
- result[resultLen] = name[:len(name)-8]
- resultLen++
- }
- return result[:resultLen], nil
- }
- func writeContentsToFileOrDie(contents []byte, path string) {
- if err := ioutil.WriteFile(path, contents, 0600); err != nil {
- panic("ioutil.WriteFile: " + err.Error())
- }
- }
- func readFileOrDie(path string) []byte {
- contents, err := ioutil.ReadFile(path)
- if err != nil {
- panic("ioutil.ReadFile: " + err.Error())
- }
- return contents
- }
- // cleanOutput transforms the supplied output so that it no longer contains
- // information that changes from run to run, making the golden tests less
- // flaky.
- func cleanOutput(o []byte, testPkg string) []byte {
- // Replace references to the last component of the test package name, which
- // contains a unique number.
- o = []byte(strings.Replace(string(o), path.Base(testPkg), "somepkg", -1))
- // Replace things that look like line numbers and process counters in stack
- // traces.
- stackFrameRe := regexp.MustCompile(`\t\S+\.(c|go):\d+`)
- o = stackFrameRe.ReplaceAll(o, []byte("\tsome_file.txt:0"))
- // Replace full paths in failure messages with fake paths.
- pathRe := regexp.MustCompile(`/\S+/(\w+\.(?:go|s):\d+)`)
- o = pathRe.ReplaceAll(o, []byte("/some/path/$1"))
- // Replace unstable timings in gotest fail messages.
- timingRe1 := regexp.MustCompile(`--- FAIL: .* \(\d\.\d{2}s\)`)
- o = timingRe1.ReplaceAll(o, []byte("--- FAIL: TestSomething (1.23s)"))
- timingRe2 := regexp.MustCompile(`FAIL.*somepkg\s*\d\.\d{2,}s`)
- o = timingRe2.ReplaceAll(o, []byte("FAIL somepkg 1.234s"))
- timingRe3 := regexp.MustCompile(`ok.*somepkg\s*\d\.\d{2,}s`)
- o = timingRe3.ReplaceAll(o, []byte("ok somepkg 1.234s"))
- timingRe4 := regexp.MustCompile(`SlowTest \([0-9.]+ms\)`)
- o = timingRe4.ReplaceAll(o, []byte("SlowTest (1234ms)"))
- return o
- }
- // Create a temporary package directory somewhere that 'go test' can find, and
- // return the directory and package name.
- func createTempPackageDir(caseName string) (dir, pkg string) {
- // Figure out where the local source code for ogletest is.
- buildPkg, err := build.Import(ogletestPkg, "", build.FindOnly)
- if err != nil {
- panic("Finding ogletest tree: " + err.Error())
- }
- // Create a temporary directory underneath this.
- ogletestPkgDir := buildPkg.Dir
- prefix := fmt.Sprintf("tmp-%s-", caseName)
- dir, err = ioutil.TempDir(ogletestPkgDir, prefix)
- if err != nil {
- panic("ioutil.TempDir: " + err.Error())
- }
- pkg = path.Join("github.com/smartystreets/assertions/internal/ogletest", dir[len(ogletestPkgDir):])
- return
- }
- // runTestCase runs the case with the supplied name (e.g. "passing"), and
- // returns its output and exit code.
- func runTestCase(name string) ([]byte, int, error) {
- // Create a temporary directory for the test files.
- testDir, testPkg := createTempPackageDir(name)
- defer os.RemoveAll(testDir)
- // Create the test source file.
- sourceFile := name + ".test.go"
- testContents := readFileOrDie(path.Join("test_cases", sourceFile))
- writeContentsToFileOrDie(testContents, path.Join(testDir, name+"_test.go"))
- // Invoke 'go test'. Use the package directory as working dir instead of
- // giving the package name as an argument so that 'go test' prints passing
- // test output. Special case: pass a test filter to the filtered case.
- cmd := exec.Command("go", "test")
- if name == "filtered" {
- cmd.Args = append(cmd.Args, "--ogletest.run=Test(Bar|Baz)")
- }
- cmd.Dir = testDir
- output, err := cmd.CombinedOutput()
- // Clean up the process's output.
- output = cleanOutput(output, testPkg)
- // Did the process exist with zero code?
- if err == nil {
- return output, 0, nil
- }
- // Make sure the process actually exited.
- exitError, ok := err.(*exec.ExitError)
- if !ok || !exitError.Exited() {
- return nil, 0, errors.New("exec.Command.Output: " + err.Error())
- }
- return output, exitError.Sys().(syscall.WaitStatus).ExitStatus(), nil
- }
- // checkGolden file checks the supplied actual output for the named test case
- // against the golden file for that case. If requested by the user, it rewrites
- // the golden file on failure.
- func checkAgainstGoldenFile(caseName string, output []byte) bool {
- goldenFile := path.Join("test_cases", "golden."+caseName+"_test")
- goldenContents := readFileOrDie(goldenFile)
- result := string(output) == string(goldenContents)
- if !result && *dumpNew {
- writeContentsToFileOrDie(output, goldenFile)
- }
- return result
- }
- ////////////////////////////////////////////////////////////////////////
- // Tests
- ////////////////////////////////////////////////////////////////////////
- func TestGoldenFiles(t *testing.T) {
- // Ensure the local package is installed. This will prevent the test cases
- // from using the installed version, which may be out of date.
- err := installLocalPackages()
- if err != nil {
- t.Fatalf("Error installing local ogletest: %v", err)
- }
- // We expect there to be at least one case.
- caseNames, err := getCaseNames()
- if err != nil || len(caseNames) == 0 {
- t.Fatalf("Error getting cases: %v", err)
- }
- // Run each test case.
- for _, caseName := range caseNames {
- // Run the test case.
- output, exitCode, err := runTestCase(caseName)
- if err != nil {
- t.Fatalf("Running test case %s: %v", caseName, err)
- }
- // Check the status code. We assume all test cases fail except for the
- // passing one.
- shouldPass := (caseName == "passing" || caseName == "no_cases")
- didPass := exitCode == 0
- if shouldPass != didPass {
- t.Errorf("Bad exit code for test case %s: %d", caseName, exitCode)
- }
- // Check the output against the golden file.
- if !checkAgainstGoldenFile(caseName, output) {
- t.Errorf("Output for test case %s doesn't match golden file.", caseName)
- }
- }
- }