kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/platform/kzip/kzip_test.go (about) 1 /* 2 * Copyright 2018 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Package kzip_test is an external unit test for package kzip. 18 package kzip_test 19 20 import ( 21 "archive/zip" 22 "bytes" 23 "errors" 24 "fmt" 25 "io/ioutil" 26 "path/filepath" 27 "strings" 28 "testing" 29 30 "kythe.io/kythe/go/test/testutil" 31 32 "google.golang.org/protobuf/proto" 33 "kythe.io/kythe/go/platform/kzip" 34 35 apb "kythe.io/kythe/proto/analysis_go_proto" 36 spb "kythe.io/kythe/proto/storage_go_proto" 37 ) 38 39 func TestRoundTrip_Proto(t *testing.T) { 40 testRoundTrip(kzip.EncodingProto, t) 41 } 42 43 func TestRoundTrip_All(t *testing.T) { 44 testRoundTrip(kzip.EncodingAll, t) 45 } 46 47 func TestRoundTrip_JSON(t *testing.T) { 48 testRoundTrip(kzip.EncodingJSON, t) 49 } 50 51 func testRoundTrip(encoding kzip.Encoding, t *testing.T) { 52 buf := bytes.NewBuffer(nil) 53 54 // Create a kzip with some interesting data. 55 w, err := kzip.NewWriter(buf, kzip.WithEncoding(encoding)) 56 if err != nil { 57 t.Fatalf("NewWriter: unexpected error: %v", err) 58 } 59 60 // Write a compilation record with some index data. 61 unitIn := &apb.CompilationUnit{ 62 VName: &spb.VName{Corpus: "foo", Language: "bar"}, 63 SourceFile: []string{"blodgit"}, 64 } 65 indexIn := &apb.IndexedCompilation_Index{ 66 Revisions: []string{"a", "b", "c"}, 67 } 68 udigest, err := w.AddUnit(unitIn, indexIn) 69 if err != nil { 70 t.Errorf("AddUnit: unexpected error: %v", err) 71 } 72 t.Logf("Unit digest: %q", udigest) 73 74 // Check that rewriting the same record reports the correct error, and 75 // doesn't clobber the previous one, but returns the same digest. 76 if cmp, err := w.AddUnit(unitIn, nil); err != kzip.ErrUnitExists { 77 t.Errorf("AddUnit (again): got error %v, want %v", err, kzip.ErrUnitExists) 78 } else if cmp != udigest { 79 t.Errorf("AddUnit (again): got digest %q, want %q", cmp, udigest) 80 } 81 82 // Write a file record. 83 const fileIn = "aaa\n" 84 fdigest, err := w.AddFile(strings.NewReader(fileIn)) 85 if err != nil { 86 t.Errorf("AddFile %q: unexpected error: %v", fileIn, err) 87 } 88 t.Logf("File digest: %q", fdigest) 89 90 // Check that the file got hashed correctly. 91 // echo "aaa" | sha256sum 92 const wantFileDigest = "17e682f060b5f8e47ea04c5c4855908b0a5ad612022260fe50e11ecb0cc0ab76" 93 if fdigest != wantFileDigest { 94 t.Errorf("AddFile reported the wrong digest: got %q, want %q", fdigest, wantFileDigest) 95 } 96 97 if err := w.Close(); err != nil { 98 t.Errorf("Writer.Close: unexpected error: %v", err) 99 } 100 101 // Open a reader on the data produced by the writer. 102 r, err := kzip.NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len())) 103 if err != nil { 104 t.Errorf("NewReader: unexpected error: %v", err) 105 } 106 107 // Verify that we can read the original unit back out. 108 if u, err := r.Lookup(udigest); err != nil { 109 t.Errorf("Lookup %q: unexpected error: %v", udigest, err) 110 } else { 111 if !proto.Equal(u.Proto, unitIn) { 112 t.Errorf("Lookup unit: got %+v, want %+v", u.Proto, unitIn) 113 } 114 if !proto.Equal(u.Index, indexIn) { 115 t.Errorf("Lookup index: got %+v, want %+v", u.Index, indexIn) 116 } 117 } 118 119 // Verify that a non-existing unit digest reports ErrDigestNotFound. 120 if u, err := r.Lookup("does not exist"); err != kzip.ErrDigestNotFound { 121 t.Errorf("Lookup (non-existing unit): got %+v and error %v, want %v", 122 u, err, kzip.ErrDigestNotFound) 123 } 124 125 // Verify that we can read the original file back out. 126 if bits, err := r.ReadAll(fdigest); err != nil { 127 t.Errorf("ReadAll %q: unexpected error: %v", fdigest, err) 128 } else if got := string(bits); got != fileIn { 129 t.Errorf("ReadAll %q: got %q, want %q", fdigest, got, fileIn) 130 } 131 132 // Verify that a non-existing file digest reports ErrDigestNotFound. 133 if f, err := r.Open("does not exist"); err != kzip.ErrDigestNotFound { 134 t.Errorf("Open (non-existing file): got error %v, want %v", err, kzip.ErrDigestNotFound) 135 if err == nil { 136 f.Close() 137 } 138 } 139 140 // Verify that scanning works. 141 ok := false 142 if err := r.Scan(func(u *kzip.Unit) error { 143 t.Logf("Scan found %#v", u) 144 if ok { 145 return errors.New("multiple units found during scan (wanted 1)") 146 } 147 ok = u.Digest == udigest && proto.Equal(u.Proto, unitIn) && proto.Equal(u.Index, indexIn) 148 return nil 149 }); err != nil { 150 t.Errorf("Scan failed: %v", err) 151 } 152 if !ok { 153 t.Errorf("Scan did not locate the stored compilation %+v", unitIn) 154 } 155 } 156 157 // bufferStub implements io.WriteCloser and records when it has been closed to 158 // verify that closes are propagated correctly. 159 type bufferStub struct { 160 writeErr error 161 closeErr error 162 closed bool 163 } 164 165 func (b *bufferStub) Write(data []byte) (int, error) { 166 if b.writeErr != nil { 167 return 0, b.writeErr 168 } 169 return len(data), nil 170 } 171 172 func (b *bufferStub) Close() error { 173 b.closed = true 174 return b.closeErr 175 } 176 177 func TestWriteCloser(t *testing.T) { 178 wbad := errors.New("the bad thing happened while writing") 179 cbad := errors.New("the bad thing happened while closing") 180 tests := []struct { 181 write, close, want error 182 }{ 183 {nil, nil, nil}, // no harm, no foul 184 {wbad, nil, wbad}, // write errors propagate even if close is OK 185 {nil, cbad, cbad}, // a close error doesn't get swallowed 186 {wbad, cbad, wbad}, // write errors are preferred (primacy) 187 } 188 for _, test := range tests { 189 stub := &bufferStub{writeErr: test.write, closeErr: test.close} 190 w, err := kzip.NewWriteCloser(stub) 191 if err != nil { 192 t.Errorf("NewWriteCloser(%+v) failed: %v", test, err) 193 continue 194 } 195 if got := w.Close(); got != test.want { 196 t.Errorf("w.Close(): got %v, want %v", got, test.want) 197 } 198 if !stub.closed { 199 t.Error("Stub was not correctly closed") 200 } 201 } 202 } 203 204 func TestErrors(t *testing.T) { 205 // A structurally invalid zip stream should complain suitably. 206 if r, err := kzip.NewReader(bytes.NewReader(nil), 0); err == nil { 207 t.Errorf("NewReader (invalid): got %+v, want error", r) 208 } else { 209 t.Logf("NewReader (invalid): error OK: %v", err) 210 } 211 212 { // An empty archive should fail at open. 213 buf := bytes.NewBuffer(nil) 214 if err := zip.NewWriter(buf).Close(); err != nil { 215 t.Fatalf("Creating empty ZIP file failed: %v", err) 216 } 217 if r, err := kzip.NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len())); err == nil { 218 t.Errorf("NewReader (empty): got %+v, want error", r) 219 } else { 220 t.Logf("NewReader (empty): error OK: %v", err) 221 } 222 } 223 224 { // An archive should have a leading root directory 225 buf := bytes.NewBuffer(nil) 226 w := zip.NewWriter(buf) 227 f, err := w.Create("not-a-directory") 228 if err != nil { 229 t.Fatalf("Creating dummy file: %v", err) 230 } 231 fmt.Fprintln(f, "this is not a pipe") 232 if err := w.Close(); err != nil { 233 t.Fatalf("Closing dummy file: %v", err) 234 } 235 if r, err := kzip.NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len())); err == nil { 236 t.Errorf("NewReader (file): got %+v, want error", r) 237 } else { 238 t.Logf("NewReader (file): error OK: %v", err) 239 } 240 } 241 } 242 243 func TestScanHelper(t *testing.T) { 244 buf := bytes.NewBuffer(nil) 245 w, err := kzip.NewWriter(buf) 246 if err != nil { 247 t.Fatalf("Creating kzip writer: %v", err) 248 } 249 250 const fileData = "fweep" 251 fdigest, err := w.AddFile(strings.NewReader(fileData)) 252 if err != nil { 253 t.Fatalf("AddFile failed: %v", err) 254 } 255 256 udigest, err := w.AddUnit(&apb.CompilationUnit{ 257 OutputKey: fdigest, 258 }, &apb.IndexedCompilation_Index{ 259 Revisions: []string{"alphawozzle"}, 260 }) 261 if err != nil { 262 t.Fatalf("AddUnit failed: %v", err) 263 } 264 265 if err := w.Close(); err != nil { 266 t.Fatalf("Closing kzip: %v", err) 267 } 268 269 var numUnits int 270 if err := kzip.Scan(bytes.NewReader(buf.Bytes()), func(r *kzip.Reader, unit *kzip.Unit) error { 271 numUnits++ 272 if unit.Digest != udigest { 273 t.Errorf("Unit digest: got %q, want %q", unit.Digest, udigest) 274 } 275 if bits, err := r.ReadAll(unit.Proto.GetOutputKey()); err != nil { 276 t.Errorf("ReadAll %q failed: %v", unit.Proto.GetOutputKey(), err) 277 } else if got := string(bits); got != fileData { 278 t.Errorf("ReadAll %q: got %q, want %q", unit.Proto.GetOutputKey(), got, fileData) 279 } 280 return nil 281 }); err != nil { 282 t.Errorf("Scan failed: %v", err) 283 } 284 if numUnits != 1 { 285 t.Errorf("Scan found %d units, want 1", numUnits) 286 } 287 } 288 289 func TestScanError(t *testing.T) { 290 buf := bytes.NewBuffer(nil) 291 w, err := kzip.NewWriter(buf) 292 if err != nil { 293 t.Fatalf("Creating kzip writer: %v", err) 294 } 295 296 const fileData = "fweep" 297 fdigest, err := w.AddFile(strings.NewReader(fileData)) 298 if err != nil { 299 t.Fatalf("AddFile failed: %v", err) 300 } 301 302 if _, err := w.AddUnit(&apb.CompilationUnit{ 303 OutputKey: fdigest, 304 }, &apb.IndexedCompilation_Index{ 305 Revisions: []string{"alphawozzle"}, 306 }); err != nil { 307 t.Fatalf("AddUnit failed: %v", err) 308 } 309 310 if _, err := w.AddUnit(&apb.CompilationUnit{ 311 OutputKey: fdigest + "2", 312 }, &apb.IndexedCompilation_Index{ 313 Revisions: []string{"alphawozzle"}, 314 }); err != nil { 315 t.Fatalf("AddUnit failed: %v", err) 316 } 317 318 if err := w.Close(); err != nil { 319 t.Fatalf("Closing kzip: %v", err) 320 } 321 322 expected := errors.New("expected error") 323 var unitCount int 324 if err := kzip.Scan(bytes.NewReader(buf.Bytes()), func(r *kzip.Reader, unit *kzip.Unit) error { 325 unitCount++ 326 return expected 327 }); err == nil { 328 t.Errorf("Scan succeeded unexpectedly") 329 } else if err != expected { 330 t.Errorf("Scan failed unexpectedly: %v", err) 331 } else if unitCount != 1 { 332 t.Errorf("Scanned %d units; expected: 1", unitCount) 333 } 334 } 335 336 func TestScanConcurrency(t *testing.T) { 337 buf := bytes.NewBuffer(nil) 338 w, err := kzip.NewWriter(buf) 339 if err != nil { 340 t.Fatalf("Creating kzip writer: %v", err) 341 } 342 343 const fileData = "fweep" 344 fdigest, err := w.AddFile(strings.NewReader(fileData)) 345 if err != nil { 346 t.Fatalf("AddFile failed: %v", err) 347 } 348 349 const N = 128 350 for i := 0; i < N; i++ { 351 if _, err := w.AddUnit(&apb.CompilationUnit{ 352 OutputKey: fmt.Sprintf("%s%d", fdigest, i), 353 }, &apb.IndexedCompilation_Index{ 354 Revisions: []string{"alphawozzle"}, 355 }); err != nil { 356 t.Fatalf("AddUnit %d failed: %v", i, err) 357 } 358 } 359 360 if err := w.Close(); err != nil { 361 t.Fatalf("Closing kzip: %v", err) 362 } 363 364 var numUnits int 365 if err := kzip.Scan(bytes.NewReader(buf.Bytes()), func(r *kzip.Reader, unit *kzip.Unit) error { 366 numUnits++ 367 return nil 368 }, kzip.ReadConcurrency(16)); err != nil { 369 t.Errorf("Scan failed: %v", err) 370 } 371 if numUnits != N { 372 t.Errorf("Scan found %d units, want %d", numUnits, N) 373 } 374 } 375 376 const testDataDir = "../../../testdata/platform" 377 378 func TestMissingJSONUnitFails(t *testing.T) { 379 b, err := ioutil.ReadFile(testutil.TestFilePath(t, filepath.Join(testDataDir, "missing-unit.kzip"))) 380 if err != nil { 381 t.Fatalf("Unable to read test file missing-unit.kzip: %s", err) 382 } 383 _, err = kzip.NewReader(bytes.NewReader(b), int64(len(b))) 384 if err == nil || err.Error() != "both proto and JSON units found but are not identical" { 385 t.Errorf("Unexpected error: %s", err) 386 } 387 } 388 389 func TestMissingProtoUnitFails(t *testing.T) { 390 b, err := ioutil.ReadFile(testutil.TestFilePath(t, filepath.Join(testDataDir, "missing-pbunit.kzip"))) 391 if err != nil { 392 t.Fatalf("Unable to read test file missing-pbunit.kzip: %s", err) 393 } 394 _, err = kzip.NewReader(bytes.NewReader(b), int64(len(b))) 395 if err == nil || err.Error() != "both proto and JSON units found but are not identical" { 396 t.Errorf("Unexpected error: %s", err) 397 } 398 }