mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-03 23:40:03 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			326 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			326 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
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)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |