github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/covermerger/bq_csv_reader_test.go (about)

     1  // Copyright 2025 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package covermerger
     5  
     6  import (
     7  	"bytes"
     8  	"compress/gzip"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"testing"
    13  
    14  	"github.com/google/syzkaller/pkg/gcs"
    15  	gcsmocks "github.com/google/syzkaller/pkg/gcs/mocks"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/mock"
    18  )
    19  
    20  func TestGCSGZIPMultiReader_Read(t *testing.T) {
    21  	tests := []struct {
    22  		name       string
    23  		inputFiles []string
    24  		inputBytes [][]byte
    25  
    26  		wantBytes []byte
    27  		wantErr   error
    28  	}{
    29  		{
    30  			name:       "single file, single read",
    31  			inputFiles: []string{"file1"},
    32  			inputBytes: [][]byte{gzBytes("1")},
    33  			wantBytes:  []byte("1"),
    34  			wantErr:    nil,
    35  		},
    36  		{
    37  			name:       "single file, multiple reads",
    38  			inputFiles: []string{"file1"},
    39  			inputBytes: [][]byte{gzBytes("123")},
    40  			wantBytes:  []byte("123"),
    41  			wantErr:    nil,
    42  		},
    43  		{
    44  			name:       "multiple files, multiple reads",
    45  			inputFiles: []string{"file1", "file2", "file3"},
    46  			inputBytes: [][]byte{gzBytes("123"), gzBytes("456"), gzBytes("789")},
    47  			wantBytes:  []byte("123456789"),
    48  			wantErr:    nil,
    49  		},
    50  		{
    51  			name:       "multiple files, badbytes",
    52  			inputFiles: []string{"file1", "file2", "file3"},
    53  			inputBytes: [][]byte{gzBytes("123"), gzBytes("456"), []byte("789")},
    54  			wantBytes:  []byte("123456"),
    55  			wantErr:    fmt.Errorf("err calling gzip.NewReader: %w", errors.New("unexpected EOF")),
    56  		},
    57  	}
    58  
    59  	for _, test := range tests {
    60  		t.Run(test.name, func(t *testing.T) {
    61  			mr := &gcsGZIPMultiReader{
    62  				gcsClient: makeGCSClientMock(t, test.inputFiles, test.inputBytes),
    63  				gcsFiles:  test.inputFiles,
    64  			}
    65  			gotBytes, gotErr := io.ReadAll(mr)
    66  			assert.NoError(t, mr.Close())
    67  			assert.Equal(t, test.wantErr, gotErr)
    68  			assert.Equal(t, test.wantBytes, gotBytes)
    69  		})
    70  	}
    71  }
    72  
    73  func makeGCSClientMock(t *testing.T, files []string, bytes [][]byte) gcs.Client {
    74  	gcsClientMock := gcsmocks.NewClient(t)
    75  	for i, file := range files {
    76  		rcMock := &readCloserMock{}
    77  		for _, b := range bytes[i] {
    78  			rcMock.On("Read", mock.Anything).
    79  				Run(func(args mock.Arguments) {
    80  					arg := args.Get(0).([]byte)
    81  					arg[0] = b
    82  				}).
    83  				Return(1, nil).Once()
    84  		}
    85  		rcMock.On("Read", mock.Anything).
    86  			Return(0, io.EOF).
    87  			On("Close").
    88  			Return(nil).Once()
    89  
    90  		gcsClientMock.EXPECT().
    91  			FileReader(file).
    92  			Return(rcMock, nil)
    93  	}
    94  	return gcsClientMock
    95  }
    96  
    97  type readCloserMock struct {
    98  	mock.Mock
    99  }
   100  
   101  func (m *readCloserMock) Read(p []byte) (n int, err error) {
   102  	args := m.Called(p)
   103  	return args.Int(0), args.Error(1)
   104  }
   105  
   106  func (m *readCloserMock) Close() (err error) {
   107  	args := m.Called()
   108  	return args.Error(0)
   109  }
   110  
   111  func gzBytes(str string) []byte {
   112  	buf := &bytes.Buffer{}
   113  	gzw := gzip.NewWriter(buf)
   114  	gzw.Write([]byte(str))
   115  	gzw.Close()
   116  	return buf.Bytes()
   117  }