1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package zip_sum_test
17
18 import (
19 "context"
20 "crypto/sha256"
21 "encoding/csv"
22 "encoding/hex"
23 "flag"
24 "fmt"
25 "internal/testenv"
26 "io"
27 "os"
28 "path/filepath"
29 "strings"
30 "testing"
31
32 "cmd/go/internal/cfg"
33 "cmd/go/internal/modfetch"
34 "cmd/go/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
58
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
73
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 := os.MkdirTemp("", "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
100
101
102
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
115
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 ctx := context.Background()
123
124 zipPath, err := modfetch.DownloadZip(ctx, test.m)
125 if err != nil {
126 if *updateTestData {
127 t.Logf("%s: could not download module: %s (will remove from testdata)", test.m, err)
128 test.m.Path = ""
129 needUpdate = true
130 } else {
131 t.Errorf("%s: could not download module: %s", test.m, err)
132 }
133 return
134 }
135
136 sum := modfetch.Sum(ctx, test.m)
137 if sum != test.wantSum {
138 if *updateTestData {
139 t.Logf("%s: updating content sum to %s", test.m, sum)
140 test.wantSum = sum
141 needUpdate = true
142 } else {
143 t.Errorf("%s: got content sum %s; want sum %s", test.m, sum, test.wantSum)
144 return
145 }
146 }
147
148 h := sha256.New()
149 f, err := os.Open(zipPath)
150 if err != nil {
151 t.Errorf("%s: %v", test.m, err)
152 }
153 defer f.Close()
154 if _, err := io.Copy(h, f); err != nil {
155 t.Errorf("%s: %v", test.m, err)
156 }
157 zipHash := hex.EncodeToString(h.Sum(nil))
158 if zipHash != test.wantFileHash {
159 if *updateTestData {
160 t.Logf("%s: updating zip file hash to %s", test.m, zipHash)
161 test.wantFileHash = zipHash
162 needUpdate = true
163 } else {
164 t.Errorf("%s: got zip file hash %s; want hash %s (but content sum matches)", test.m, zipHash, test.wantFileHash)
165 }
166 }
167 })
168 }
169
170 if needUpdate {
171
172 r, w := 0, 0
173 for r < len(tests) {
174 if tests[r].m.Path != "" {
175 tests[w] = tests[r]
176 w++
177 }
178 r++
179 }
180 tests = tests[:w]
181
182 if err := writeZipSumTests(tests); err != nil {
183 t.Error(err)
184 }
185 }
186 }
187
188 func readZipSumTests() ([]zipSumTest, error) {
189 f, err := os.Open(filepath.FromSlash(zipSumsPath))
190 if err != nil {
191 return nil, err
192 }
193 defer f.Close()
194 r := csv.NewReader(f)
195
196 var tests []zipSumTest
197 for {
198 line, err := r.Read()
199 if err == io.EOF {
200 break
201 } else if err != nil {
202 return nil, err
203 } else if len(line) != 4 {
204 return nil, fmt.Errorf("%s:%d: malformed line", f.Name(), len(tests)+1)
205 }
206 test := zipSumTest{m: module.Version{Path: line[0], Version: line[1]}, wantSum: line[2], wantFileHash: line[3]}
207 tests = append(tests, test)
208 }
209 return tests, nil
210 }
211
212 func writeZipSumTests(tests []zipSumTest) (err error) {
213 f, err := os.Create(filepath.FromSlash(zipSumsPath))
214 if err != nil {
215 return err
216 }
217 defer func() {
218 if cerr := f.Close(); err == nil && cerr != nil {
219 err = cerr
220 }
221 }()
222 w := csv.NewWriter(f)
223 line := make([]string, 0, 4)
224 for _, test := range tests {
225 line = append(line[:0], test.m.Path, test.m.Version, test.wantSum, test.wantFileHash)
226 if err := w.Write(line); err != nil {
227 return err
228 }
229 }
230 w.Flush()
231 return nil
232 }
233
View as plain text