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  }