mirror of
https://github.com/golang/go
synced 2024-11-06 20:36:14 -07:00
cmd/go/internal/modfetch/zip_sum_test: long test for zip sum stability
This CL adds a new test package which downloads specific versions of ~1000 modules in direct mode and verifies that modules have the same sums and the zip files have the same SHA-256 hashes. This test takes a long time to run and depends heavily on external data that may disappear. It must be enabled manually with -zipsum. Fixes #35290 Change-Id: Ic6959e685096e8b09cea291f19d5bd0255432284 Reviewed-on: https://go-review.googlesource.com/c/go/+/204838 Reviewed-by: Bryan C. Mills <bcmills@google.com>
This commit is contained in:
parent
73d57bf80f
commit
f7d0a42121
2124
src/cmd/go/internal/modfetch/zip_sum_test/testdata/zip_sums.csv
vendored
Normal file
2124
src/cmd/go/internal/modfetch/zip_sum_test/testdata/zip_sums.csv
vendored
Normal file
File diff suppressed because it is too large
Load Diff
230
src/cmd/go/internal/modfetch/zip_sum_test/zip_sum_test.go
Normal file
230
src/cmd/go/internal/modfetch/zip_sum_test/zip_sum_test.go
Normal file
@ -0,0 +1,230 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package zip_sum_test tests that the module zip files produced by modfetch
|
||||
// have consistent content sums. Ideally the zip files themselves are also
|
||||
// stable over time, though this is not strictly necessary.
|
||||
//
|
||||
// This test loads a table from testdata/zip_sums.csv. The table has columns
|
||||
// for module path, version, content sum, and zip file hash. The table
|
||||
// includes a large number of real modules. The test downloads these modules
|
||||
// in direct mode and verifies the zip files.
|
||||
//
|
||||
// This test is very slow, and it depends on outside modules that change
|
||||
// frequently, so this is a manual test. To enable it, pass the -zipsum flag.
|
||||
package zip_sum_test
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/csv"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modload"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
var (
|
||||
updateTestData = flag.Bool("u", false, "when set, tests may update files in testdata instead of failing")
|
||||
enableZipSum = flag.Bool("zipsum", false, "enable TestZipSums")
|
||||
debugZipSum = flag.Bool("testwork", false, "when set, TestZipSums will preserve its test directory")
|
||||
modCacheDir = flag.String("zipsumcache", "", "module cache to use instead of temp directory")
|
||||
shardCount = flag.Int("zipsumshardcount", 1, "number of shards to divide TestZipSums into")
|
||||
shardIndex = flag.Int("zipsumshard", 0, "index of TestZipSums shard to test (0 <= zipsumshard < zipsumshardcount)")
|
||||
)
|
||||
|
||||
const zipSumsPath = "testdata/zip_sums.csv"
|
||||
|
||||
type zipSumTest struct {
|
||||
m module.Version
|
||||
wantSum, wantFileHash string
|
||||
}
|
||||
|
||||
func TestZipSums(t *testing.T) {
|
||||
if !*enableZipSum {
|
||||
// This test is very slow and heavily dependent on external repositories.
|
||||
// Only run it explicitly.
|
||||
t.Skip("TestZipSum not enabled with -zipsum")
|
||||
}
|
||||
if *shardCount < 1 {
|
||||
t.Fatal("-zipsumshardcount must be a positive integer")
|
||||
}
|
||||
if *shardIndex < 0 || *shardCount <= *shardIndex {
|
||||
t.Fatal("-zipsumshard must be between 0 and -zipsumshardcount")
|
||||
}
|
||||
|
||||
testenv.MustHaveGoBuild(t)
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
testenv.MustHaveExecPath(t, "bzr")
|
||||
testenv.MustHaveExecPath(t, "git")
|
||||
// TODO(jayconrod): add hg, svn, and fossil modules to testdata.
|
||||
// Could not find any for now.
|
||||
|
||||
tests, err := readZipSumTests()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if *modCacheDir != "" {
|
||||
cfg.BuildContext.GOPATH = *modCacheDir
|
||||
} else {
|
||||
tmpDir, err := ioutil.TempDir("", "TestZipSums")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if *debugZipSum {
|
||||
fmt.Fprintf(os.Stderr, "TestZipSums: modCacheDir: %s\n", tmpDir)
|
||||
} else {
|
||||
defer os.RemoveAll(tmpDir)
|
||||
}
|
||||
cfg.BuildContext.GOPATH = tmpDir
|
||||
}
|
||||
|
||||
cfg.GOPROXY = "direct"
|
||||
cfg.GOSUMDB = "off"
|
||||
modload.Init()
|
||||
|
||||
// Shard tests by downloading only every nth module when shard flags are set.
|
||||
// This makes it easier to test small groups of modules quickly. We avoid
|
||||
// testing similarly named modules together (the list is sorted by module
|
||||
// path and version).
|
||||
if *shardCount > 1 {
|
||||
r := *shardIndex
|
||||
w := 0
|
||||
for r < len(tests) {
|
||||
tests[w] = tests[r]
|
||||
w++
|
||||
r += *shardCount
|
||||
}
|
||||
tests = tests[:w]
|
||||
}
|
||||
|
||||
// Download modules with a rate limit. We may run out of file descriptors
|
||||
// or cause timeouts without a limit.
|
||||
needUpdate := false
|
||||
for i := range tests {
|
||||
test := &tests[i]
|
||||
name := fmt.Sprintf("%s@%s", strings.ReplaceAll(test.m.Path, "/", "_"), test.m.Version)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
zipPath, err := modfetch.DownloadZip(test.m)
|
||||
if err != nil {
|
||||
if *updateTestData {
|
||||
t.Logf("%s: could not download module: %s (will remove from testdata)", test.m, err)
|
||||
test.m.Path = "" // mark for deletion
|
||||
needUpdate = true
|
||||
} else {
|
||||
t.Errorf("%s: could not download mdoule: %s", test.m, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
sum := modfetch.Sum(test.m)
|
||||
if sum != test.wantSum {
|
||||
if *updateTestData {
|
||||
t.Logf("%s: updating content sum to %s", test.m, sum)
|
||||
test.wantSum = sum
|
||||
needUpdate = true
|
||||
} else {
|
||||
t.Errorf("%s: got content sum %s; want sum %s", test.m, sum, test.wantSum)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
h := sha256.New()
|
||||
f, err := os.Open(zipPath)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", test.m, err)
|
||||
}
|
||||
defer f.Close()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
t.Errorf("%s: %v", test.m, err)
|
||||
}
|
||||
zipHash := hex.EncodeToString(h.Sum(nil))
|
||||
if zipHash != test.wantFileHash {
|
||||
if *updateTestData {
|
||||
t.Logf("%s: updating zip file hash to %s", test.m, zipHash)
|
||||
test.wantFileHash = zipHash
|
||||
needUpdate = true
|
||||
} else {
|
||||
t.Errorf("%s: got zip file hash %s; want hash %s (but content sum matches)", test.m, zipHash, test.wantFileHash)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if needUpdate {
|
||||
// Remove tests marked for deletion
|
||||
r, w := 0, 0
|
||||
for r < len(tests) {
|
||||
if tests[r].m.Path != "" {
|
||||
tests[w] = tests[r]
|
||||
w++
|
||||
}
|
||||
r++
|
||||
}
|
||||
tests = tests[:w]
|
||||
|
||||
if err := writeZipSumTests(tests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readZipSumTests() ([]zipSumTest, error) {
|
||||
f, err := os.Open(filepath.FromSlash(zipSumsPath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
r := csv.NewReader(f)
|
||||
|
||||
var tests []zipSumTest
|
||||
for {
|
||||
line, err := r.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else if len(line) != 4 {
|
||||
return nil, fmt.Errorf("%s:%d: malformed line", f.Name(), len(tests)+1)
|
||||
}
|
||||
test := zipSumTest{m: module.Version{Path: line[0], Version: line[1]}, wantSum: line[2], wantFileHash: line[3]}
|
||||
tests = append(tests, test)
|
||||
}
|
||||
return tests, nil
|
||||
}
|
||||
|
||||
func writeZipSumTests(tests []zipSumTest) (err error) {
|
||||
f, err := os.Create(filepath.FromSlash(zipSumsPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if cerr := f.Close(); err == nil && cerr != nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
w := csv.NewWriter(f)
|
||||
line := make([]string, 0, 4)
|
||||
for _, test := range tests {
|
||||
line = append(line[:0], test.m.Path, test.m.Version, test.wantSum, test.wantFileHash)
|
||||
if err := w.Write(line); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user