123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- /*
- Copyright 2016 The Kubernetes Authors.
- 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 main
- import (
- "fmt"
- "io/ioutil"
- "math/rand"
- "os"
- "path"
- "path/filepath"
- "testing"
- "time"
- )
- const (
- // seed for rand.Source to generate data for files
- seed int64 = 42
- // 1K binary file
- binLen = 1024
- // Directory of the test package relative to $GOPATH
- testImportDir = "example.com/proj/pkg"
- )
- var (
- pastHour = time.Now().Add(-1 * time.Hour)
- // The test package we are testing against
- testPkg = path.Join(testImportDir, "test")
- )
- // fakegolist implements the `golist` interface providing fake package information for testing.
- type fakegolist struct {
- dir string
- importMap map[string]pkg
- testFiles []string
- binfile string
- }
- func newFakegolist() (*fakegolist, error) {
- dir, err := ioutil.TempDir("", "teststale")
- if err != nil {
- // test can't proceed without a temp directory.
- return nil, fmt.Errorf("failed to create a temp directory for testing: %v", err)
- }
- // Set the temp directory as the $GOPATH
- if err := os.Setenv("GOPATH", dir); err != nil {
- // can't proceed without pointing the $GOPATH to the temp directory.
- return nil, fmt.Errorf("failed to set \"$GOPATH\" pointing to %q: %v", dir, err)
- }
- // Setup $GOPATH directory layout.
- // Yeah! I am bored of repeatedly writing "if err != nil {}"!
- if os.MkdirAll(filepath.Join(dir, "bin"), 0750) != nil ||
- os.MkdirAll(filepath.Join(dir, "pkg", "linux_amd64"), 0750) != nil ||
- os.MkdirAll(filepath.Join(dir, "src"), 0750) != nil {
- return nil, fmt.Errorf("failed to setup the $GOPATH directory structure")
- }
- // Create a temp file to represent the test binary.
- binfile, err := ioutil.TempFile("", "testbin")
- if err != nil {
- return nil, fmt.Errorf("failed to create the temp file to represent the test binary: %v", err)
- }
- // Could have used crypto/rand instead, but it doesn't matter.
- rr := rand.New(rand.NewSource(42))
- bin := make([]byte, binLen)
- if _, err = rr.Read(bin); err != nil {
- return nil, fmt.Errorf("couldn't read from the random source: %v", err)
- }
- if _, err := binfile.Write(bin); err != nil {
- return nil, fmt.Errorf("couldn't write to the binary file %q: %v", binfile.Name(), err)
- }
- if err := binfile.Close(); err != nil {
- // It is arguable whether this should be fatal.
- return nil, fmt.Errorf("failed to close the binary file %q: %v", binfile.Name(), err)
- }
- if err := os.Chtimes(binfile.Name(), time.Now(), time.Now()); err != nil {
- return nil, fmt.Errorf("failed to modify the mtime of the binary file %q: %v", binfile.Name(), err)
- }
- // Create test source files directory.
- testdir := filepath.Join(dir, "src", testPkg)
- if err := os.MkdirAll(testdir, 0750); err != nil {
- return nil, fmt.Errorf("failed to create test source directory %q: %v", testdir, err)
- }
- fgl := &fakegolist{
- dir: dir,
- importMap: map[string]pkg{
- "example.com/proj/pkg/test": {
- Dir: path.Join(dir, "src", testPkg),
- ImportPath: testPkg,
- Target: path.Join(dir, "pkg", "linux_amd64", testImportDir, "test.a"),
- Stale: false,
- TestGoFiles: []string{
- "foo_test.go",
- "bar_test.go",
- },
- TestImports: []string{
- "example.com/proj/pkg/p1",
- "example.com/proj/pkg/p1/c11",
- "example.com/proj/pkg/p2",
- "example.com/proj/cmd/p3/c12/c23",
- "strings",
- "testing",
- },
- XTestGoFiles: []string{
- "xfoo_test.go",
- "xbar_test.go",
- "xbaz_test.go",
- },
- XTestImports: []string{
- "example.com/proj/pkg/test",
- "example.com/proj/pkg/p1",
- "example.com/proj/cmd/p3/c12/c23",
- "os",
- "testing",
- },
- },
- "example.com/proj/pkg/p1": {Stale: false},
- "example.com/proj/pkg/p1/c11": {Stale: false},
- "example.com/proj/pkg/p2": {Stale: false},
- "example.com/proj/cmd/p3/c12/c23": {Stale: false},
- "strings": {Stale: false},
- "testing": {Stale: false},
- "os": {Stale: false},
- },
- testFiles: []string{
- "foo_test.go",
- "bar_test.go",
- "xfoo_test.go",
- "xbar_test.go",
- "xbaz_test.go",
- },
- binfile: binfile.Name(),
- }
- // Create test source files.
- for _, fn := range fgl.testFiles {
- fp := filepath.Join(testdir, fn)
- if _, err := os.Create(fp); err != nil {
- return nil, fmt.Errorf("failed to create the test file %q: %v", fp, err)
- }
- if err := os.Chtimes(fp, time.Now(), pastHour); err != nil {
- return nil, fmt.Errorf("failed to modify the mtime of the test file %q: %v", binfile.Name(), err)
- }
- }
- return fgl, nil
- }
- func (fgl *fakegolist) pkgInfo(pkgPaths []string) ([]pkg, error) {
- var pkgs []pkg
- for _, path := range pkgPaths {
- p, ok := fgl.importMap[path]
- if !ok {
- return nil, fmt.Errorf("package %q not found", path)
- }
- pkgs = append(pkgs, p)
- }
- return pkgs, nil
- }
- func (fgl *fakegolist) chMtime(filename string, mtime time.Time) error {
- for _, fn := range fgl.testFiles {
- if fn == filename {
- fp := filepath.Join(fgl.dir, "src", testPkg, fn)
- if err := os.Chtimes(fp, time.Now(), mtime); err != nil {
- return fmt.Errorf("failed to modify the mtime of %q: %v", filename, err)
- }
- return nil
- }
- }
- return fmt.Errorf("file %q not found", filename)
- }
- func (fgl *fakegolist) chStale(pkg string, stale bool) error {
- if p, ok := fgl.importMap[pkg]; ok {
- p.Stale = stale
- fgl.importMap[pkg] = p
- return nil
- }
- return fmt.Errorf("package %q not found", pkg)
- }
- func (fgl *fakegolist) cleanup() {
- os.RemoveAll(fgl.dir)
- os.Remove(fgl.binfile)
- }
- func TestIsTestStale(t *testing.T) {
- cases := []struct {
- fileMtime map[string]time.Time
- pkgStaleness map[string]bool
- result bool
- }{
- // Basic test: binary is fresh, all modifications were before the binary was built.
- {
- result: false,
- },
- // A local test file is new, hence binary must be stale.
- {
- fileMtime: map[string]time.Time{
- "foo_test.go": time.Now().Add(1 * time.Hour),
- },
- result: true,
- },
- // Test package is new, so binary must be stale.
- {
- pkgStaleness: map[string]bool{
- "example.com/proj/pkg/test": true,
- },
- result: true,
- },
- // Test package dependencies are new, so binary must be stale.
- {
- pkgStaleness: map[string]bool{
- "example.com/proj/cmd/p3/c12/c23": true,
- "strings": true,
- },
- result: true,
- },
- // External test files are new, hence binary must be stale.
- {
- fileMtime: map[string]time.Time{
- "xfoo_test.go": time.Now().Add(1 * time.Hour),
- "xbar_test.go": time.Now().Add(2 * time.Hour),
- },
- result: true,
- },
- // External test dependency is new, so binary must be stale.
- {
- pkgStaleness: map[string]bool{
- "os": true,
- },
- result: true,
- },
- // Multiple source files and dependencies are new, so binary must be stale.
- {
- fileMtime: map[string]time.Time{
- "foo_test.go": time.Now().Add(1 * time.Hour),
- "xfoo_test.go": time.Now().Add(2 * time.Hour),
- "xbar_test.go": time.Now().Add(3 * time.Hour),
- },
- pkgStaleness: map[string]bool{
- "example.com/proj/pkg/p1": true,
- "example.com/proj/pkg/p1/c11": true,
- "example.com/proj/pkg/p2": true,
- "example.com/proj/cmd/p3/c12/c23": true,
- "strings": true,
- "os": true,
- },
- result: true,
- },
- // Everything is new, so binary must be stale.
- {
- fileMtime: map[string]time.Time{
- "foo_test.go": time.Now().Add(3 * time.Hour),
- "bar_test.go": time.Now().Add(1 * time.Hour),
- "xfoo_test.go": time.Now().Add(2 * time.Hour),
- "xbar_test.go": time.Now().Add(1 * time.Hour),
- "xbaz_test.go": time.Now().Add(2 * time.Hour),
- },
- pkgStaleness: map[string]bool{
- "example.com/proj/pkg/p1": true,
- "example.com/proj/pkg/p1/c11": true,
- "example.com/proj/pkg/p2": true,
- "example.com/proj/cmd/p3/c12/c23": true,
- "example.com/proj/pkg/test": true,
- "strings": true,
- "testing": true,
- "os": true,
- },
- result: true,
- },
- }
- for _, tc := range cases {
- fgl, err := newFakegolist()
- if err != nil {
- t.Fatalf("failed to setup the test: %v", err)
- }
- defer fgl.cleanup()
- for fn, mtime := range tc.fileMtime {
- if err := fgl.chMtime(fn, mtime); err != nil {
- t.Fatalf("failed to change the mtime of %q: %v", fn, err)
- }
- }
- for pkg, stale := range tc.pkgStaleness {
- if err := fgl.chStale(pkg, stale); err != nil {
- t.Fatalf("failed to change the staleness of %q: %v", pkg, err)
- }
- }
- if tc.result != isTestStale(fgl, fgl.binfile, testPkg) {
- if tc.result {
- t.Errorf("Expected test package %q to be stale", testPkg)
- } else {
- t.Errorf("Expected test package %q to be not stale", testPkg)
- }
- }
- }
- }
|