github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/modfetch/zip_sum_test/zip_sum_test.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package zip_sum_test tests that the module zip files produced by modfetch 6 // have consistent content sums. Ideally the zip files themselves are also 7 // stable over time, though this is not strictly necessary. 8 // 9 // This test loads a table from testdata/zip_sums.csv. The table has columns 10 // for module path, version, content sum, and zip file hash. The table 11 // includes a large number of real modules. The test downloads these modules 12 // in direct mode and verifies the zip files. 13 // 14 // This test is very slow, and it depends on outside modules that change 15 // frequently, so this is a manual test. To enable it, pass the -zipsum flag. 16 package zip_sum_test 17 18 import ( 19 "crypto/sha256" 20 "encoding/csv" 21 "encoding/hex" 22 "flag" 23 "fmt" 24 "github.com/gagliardetto/golang-go/not-internal/testenv" 25 "io" 26 "io/ioutil" 27 "os" 28 "path/filepath" 29 "strings" 30 "testing" 31 32 "github.com/gagliardetto/golang-go/cmd/go/not-internal/cfg" 33 "github.com/gagliardetto/golang-go/cmd/go/not-internal/modfetch" 34 "github.com/gagliardetto/golang-go/cmd/go/not-internal/modload" 35 36 "golang.org/x/mod/module" 37 ) 38 39 var ( 40 updateTestData = flag.Bool("u", false, "when set, tests may update files in testdata instead of failing") 41 enableZipSum = flag.Bool("zipsum", false, "enable TestZipSums") 42 debugZipSum = flag.Bool("testwork", false, "when set, TestZipSums will preserve its test directory") 43 modCacheDir = flag.String("zipsumcache", "", "module cache to use instead of temp directory") 44 shardCount = flag.Int("zipsumshardcount", 1, "number of shards to divide TestZipSums into") 45 shardIndex = flag.Int("zipsumshard", 0, "index of TestZipSums shard to test (0 <= zipsumshard < zipsumshardcount)") 46 ) 47 48 const zipSumsPath = "testdata/zip_sums.csv" 49 50 type zipSumTest struct { 51 m module.Version 52 wantSum, wantFileHash string 53 } 54 55 func TestZipSums(t *testing.T) { 56 if !*enableZipSum { 57 // This test is very slow and heavily dependent on external repositories. 58 // Only run it explicitly. 59 t.Skip("TestZipSum not enabled with -zipsum") 60 } 61 if *shardCount < 1 { 62 t.Fatal("-zipsumshardcount must be a positive integer") 63 } 64 if *shardIndex < 0 || *shardCount <= *shardIndex { 65 t.Fatal("-zipsumshard must be between 0 and -zipsumshardcount") 66 } 67 68 testenv.MustHaveGoBuild(t) 69 testenv.MustHaveExternalNetwork(t) 70 testenv.MustHaveExecPath(t, "bzr") 71 testenv.MustHaveExecPath(t, "git") 72 // TODO(jayconrod): add hg, svn, and fossil modules to testdata. 73 // Could not find any for now. 74 75 tests, err := readZipSumTests() 76 if err != nil { 77 t.Fatal(err) 78 } 79 80 if *modCacheDir != "" { 81 cfg.BuildContext.GOPATH = *modCacheDir 82 } else { 83 tmpDir, err := ioutil.TempDir("", "TestZipSums") 84 if err != nil { 85 t.Fatal(err) 86 } 87 if *debugZipSum { 88 fmt.Fprintf(os.Stderr, "TestZipSums: modCacheDir: %s\n", tmpDir) 89 } else { 90 defer os.RemoveAll(tmpDir) 91 } 92 cfg.BuildContext.GOPATH = tmpDir 93 } 94 95 cfg.GOPROXY = "direct" 96 cfg.GOSUMDB = "off" 97 modload.Init() 98 99 // Shard tests by downloading only every nth module when shard flags are set. 100 // This makes it easier to test small groups of modules quickly. We avoid 101 // testing similarly named modules together (the list is sorted by module 102 // path and version). 103 if *shardCount > 1 { 104 r := *shardIndex 105 w := 0 106 for r < len(tests) { 107 tests[w] = tests[r] 108 w++ 109 r += *shardCount 110 } 111 tests = tests[:w] 112 } 113 114 // Download modules with a rate limit. We may run out of file descriptors 115 // or cause timeouts without a limit. 116 needUpdate := false 117 for i := range tests { 118 test := &tests[i] 119 name := fmt.Sprintf("%s@%s", strings.ReplaceAll(test.m.Path, "/", "_"), test.m.Version) 120 t.Run(name, func(t *testing.T) { 121 t.Parallel() 122 zipPath, err := modfetch.DownloadZip(test.m) 123 if err != nil { 124 if *updateTestData { 125 t.Logf("%s: could not download module: %s (will remove from testdata)", test.m, err) 126 test.m.Path = "" // mark for deletion 127 needUpdate = true 128 } else { 129 t.Errorf("%s: could not download mdoule: %s", test.m, err) 130 } 131 return 132 } 133 134 sum := modfetch.Sum(test.m) 135 if sum != test.wantSum { 136 if *updateTestData { 137 t.Logf("%s: updating content sum to %s", test.m, sum) 138 test.wantSum = sum 139 needUpdate = true 140 } else { 141 t.Errorf("%s: got content sum %s; want sum %s", test.m, sum, test.wantSum) 142 return 143 } 144 } 145 146 h := sha256.New() 147 f, err := os.Open(zipPath) 148 if err != nil { 149 t.Errorf("%s: %v", test.m, err) 150 } 151 defer f.Close() 152 if _, err := io.Copy(h, f); err != nil { 153 t.Errorf("%s: %v", test.m, err) 154 } 155 zipHash := hex.EncodeToString(h.Sum(nil)) 156 if zipHash != test.wantFileHash { 157 if *updateTestData { 158 t.Logf("%s: updating zip file hash to %s", test.m, zipHash) 159 test.wantFileHash = zipHash 160 needUpdate = true 161 } else { 162 t.Errorf("%s: got zip file hash %s; want hash %s (but content sum matches)", test.m, zipHash, test.wantFileHash) 163 } 164 } 165 }) 166 } 167 168 if needUpdate { 169 // Remove tests marked for deletion 170 r, w := 0, 0 171 for r < len(tests) { 172 if tests[r].m.Path != "" { 173 tests[w] = tests[r] 174 w++ 175 } 176 r++ 177 } 178 tests = tests[:w] 179 180 if err := writeZipSumTests(tests); err != nil { 181 t.Error(err) 182 } 183 } 184 } 185 186 func readZipSumTests() ([]zipSumTest, error) { 187 f, err := os.Open(filepath.FromSlash(zipSumsPath)) 188 if err != nil { 189 return nil, err 190 } 191 defer f.Close() 192 r := csv.NewReader(f) 193 194 var tests []zipSumTest 195 for { 196 line, err := r.Read() 197 if err == io.EOF { 198 break 199 } else if err != nil { 200 return nil, err 201 } else if len(line) != 4 { 202 return nil, fmt.Errorf("%s:%d: malformed line", f.Name(), len(tests)+1) 203 } 204 test := zipSumTest{m: module.Version{Path: line[0], Version: line[1]}, wantSum: line[2], wantFileHash: line[3]} 205 tests = append(tests, test) 206 } 207 return tests, nil 208 } 209 210 func writeZipSumTests(tests []zipSumTest) (err error) { 211 f, err := os.Create(filepath.FromSlash(zipSumsPath)) 212 if err != nil { 213 return err 214 } 215 defer func() { 216 if cerr := f.Close(); err == nil && cerr != nil { 217 err = cerr 218 } 219 }() 220 w := csv.NewWriter(f) 221 line := make([]string, 0, 4) 222 for _, test := range tests { 223 line = append(line[:0], test.m.Path, test.m.Version, test.wantSum, test.wantFileHash) 224 if err := w.Write(line); err != nil { 225 return err 226 } 227 } 228 w.Flush() 229 return nil 230 }