Skip to content

Commit f860164

Browse files
committed
pkg/syz-ci: test func UploadCoverJSONLToGCS
1 parent 8b79941 commit f860164

File tree

2 files changed

+152
-10
lines changed

2 files changed

+152
-10
lines changed

syz-ci/manager.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,7 @@ func (mgr *Manager) uploadCoverReport() error {
866866
}
867867
defer resp.Body.Close()
868868
if directUpload {
869-
return uploadFile(mgr.cfg.CoverUploadPath, mgr.name+".html", resp.Body, mgr.cfg.PublishGCS)
869+
return uploadFile(context.Background(), mgr.cfg.CoverUploadPath, mgr.name+".html", resp.Body, mgr.cfg.PublishGCS)
870870
}
871871
// Upload via the asset storage.
872872
newAsset, err := mgr.storage.UploadBuildAsset(resp.Body, mgr.name+".html",
@@ -881,8 +881,8 @@ func (mgr *Manager) uploadCoverReport() error {
881881
return nil
882882
}
883883

884-
func (mgr *Manager) uploadCoverJSONLToGCS(mgrSrc, gcsDest string, curTime time.Time, publish, compress bool,
885-
f func(io.Writer, *json.Decoder) error) error {
884+
func (mgr *Manager) uploadCoverJSONLToGCS(ctx context.Context, mgrSrc, gcsDest string, curTime time.Time,
885+
publish, compress bool, f func(io.Writer, *json.Decoder) error) error {
886886
if !mgr.managercfg.Cover || gcsDest == "" {
887887
return nil
888888
}
@@ -932,7 +932,7 @@ func (mgr *Manager) uploadCoverJSONLToGCS(mgrSrc, gcsDest string, curTime time.T
932932
mgr.mgrcfg.DashboardClient,
933933
mgr.name, curTime.Format(time.DateOnly),
934934
curTime.Hour(), curTime.Minute())
935-
if err := uploadFile(gcsDest, fileName, pr, publish); err != nil {
935+
if err := uploadFile(ctx, gcsDest, fileName, pr, publish); err != nil {
936936
return fmt.Errorf("failed to uploadFileGCS(): %w", err)
937937
}
938938
return nil
@@ -943,7 +943,8 @@ func (mgr *Manager) uploadCoverStat(fuzzingMinutes int) error {
943943
// In the syz-ci context report generation won't be used after this point,
944944
// so tell manager to flush report generator.
945945
curTime := time.Now()
946-
if err := mgr.uploadCoverJSONLToGCS("/cover?jsonl=1&flush=1",
946+
if err := mgr.uploadCoverJSONLToGCS(context.Background(),
947+
"/cover?jsonl=1&flush=1",
947948
mgr.cfg.CoverPipelinePath,
948949
curTime,
949950
false,
@@ -974,7 +975,8 @@ func (mgr *Manager) uploadCoverStat(fuzzingMinutes int) error {
974975
}
975976

976977
func (mgr *Manager) uploadProgramsWithCoverage() error {
977-
if err := mgr.uploadCoverJSONLToGCS("/coverprogs?jsonl=1",
978+
if err := mgr.uploadCoverJSONLToGCS(context.Background(),
979+
"/coverprogs?jsonl=1",
978980
mgr.cfg.CoverProgramsPath,
979981
time.Now(),
980982
mgr.cfg.PublishGCS,
@@ -1005,7 +1007,7 @@ func (mgr *Manager) uploadCorpus() error {
10051007
return err
10061008
}
10071009
defer f.Close()
1008-
return uploadFile(mgr.cfg.CorpusUploadPath, mgr.name+"-corpus.db", f, mgr.cfg.PublishGCS)
1010+
return uploadFile(context.Background(), mgr.cfg.CorpusUploadPath, mgr.name+"-corpus.db", f, mgr.cfg.PublishGCS)
10091011
}
10101012

10111013
func (mgr *Manager) uploadBenchData() error {
@@ -1023,15 +1025,15 @@ func (mgr *Manager) uploadBenchData() error {
10231025
return fmt.Errorf("failed to open bench file: %w", err)
10241026
}
10251027
defer f.Close()
1026-
err = uploadFile(mgr.cfg.BenchUploadPath+"/"+mgr.name,
1028+
err = uploadFile(context.Background(), mgr.cfg.BenchUploadPath+"/"+mgr.name,
10271029
mgr.lastRestarted.Format("2006-01-02_15h.json"), f, false)
10281030
if err != nil {
10291031
return fmt.Errorf("failed to upload the bench file: %w", err)
10301032
}
10311033
return nil
10321034
}
10331035

1034-
func uploadFile(dstPath, name string, file io.Reader, publish bool) error {
1036+
func uploadFile(ctx context.Context, dstPath, name string, file io.Reader, publish bool) error {
10351037
URL, err := url.Parse(dstPath)
10361038
if err != nil {
10371039
return fmt.Errorf("failed to parse upload path: %w", err)
@@ -1043,7 +1045,7 @@ func uploadFile(dstPath, name string, file io.Reader, publish bool) error {
10431045
strings.HasPrefix(URLStr, "https://") {
10441046
return uploadFileHTTPPut(URLStr, file)
10451047
}
1046-
return gcs.UploadFile(context.Background(), file, URLStr, publish)
1048+
return gcs.UploadFile(ctx, file, URLStr, publish)
10471049
}
10481050

10491051
func uploadFileHTTPPut(URL string, file io.Reader) error {

syz-ci/manager_test.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,23 @@
44
package main
55

66
import (
7+
"bytes"
8+
"compress/gzip"
9+
"context"
10+
"encoding/json"
711
"fmt"
12+
"io"
13+
"net/http"
14+
"net/http/httptest"
15+
"strings"
816
"testing"
17+
"time"
918

1019
"github.com/google/syzkaller/dashboard/dashapi"
20+
"github.com/google/syzkaller/pkg/cover"
21+
"github.com/google/syzkaller/pkg/gcs"
22+
gcsmocks "github.com/google/syzkaller/pkg/gcs/mocks"
23+
"github.com/google/syzkaller/pkg/mgrconfig"
1124
"github.com/google/syzkaller/pkg/vcs"
1225
"github.com/google/syzkaller/sys/targets"
1326
"github.com/stretchr/testify/assert"
@@ -106,3 +119,130 @@ Reported-by: [email protected]`,
106119
assert.Equal(t, commit.Title, "title with fix")
107120
assert.ElementsMatch(t, commit.BugIDs, []string{"abcd000"})
108121
}
122+
123+
func TestUploadCoverJSONLToGCS(t *testing.T) {
124+
tests := []struct {
125+
name string
126+
127+
inputJSONL string
128+
inputTime time.Time
129+
130+
inputCompress bool
131+
inputPublish bool
132+
133+
wantGCSFileName string
134+
wantGCSFileContent string
135+
wantCompressed bool
136+
wantPublish bool
137+
wantError string
138+
}{
139+
{
140+
name: "upload single object",
141+
inputJSONL: "{}",
142+
inputTime: time.Time{},
143+
wantGCSFileName: "test-bucket/test-namespace/mgr-name-0001-01-01-0-0.jsonl",
144+
wantGCSFileContent: "{}\n",
145+
},
146+
{
147+
name: "upload single object, compress",
148+
inputJSONL: "{}",
149+
inputTime: time.Time{},
150+
inputCompress: true,
151+
wantGCSFileName: "test-bucket/test-namespace/mgr-name-0001-01-01-0-0.jsonl",
152+
wantGCSFileContent: "{}\n",
153+
wantCompressed: true,
154+
},
155+
{
156+
name: "upload single object, publish",
157+
inputJSONL: "{}",
158+
inputTime: time.Time{},
159+
inputPublish: true,
160+
wantGCSFileName: "test-bucket/test-namespace/mgr-name-0001-01-01-0-0.jsonl",
161+
wantGCSFileContent: "{}\n",
162+
wantPublish: true,
163+
},
164+
{
165+
name: "upload single object, error",
166+
inputJSONL: "{",
167+
inputTime: time.Time{},
168+
wantGCSFileName: "test-bucket/test-namespace/mgr-name-0001-01-01-0-0.jsonl",
169+
wantError: "failed to uploadFileGCS(): io.Copy: callback: cover.ProgramCoverage: unexpected EOF",
170+
},
171+
}
172+
173+
for _, test := range tests {
174+
t.Run(test.name, func(t *testing.T) {
175+
httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
176+
w.Write([]byte(test.inputJSONL))
177+
}))
178+
defer httpServer.Close()
179+
180+
testSetverAddrPort, _ := strings.CutPrefix(httpServer.URL, "http://")
181+
mgr := Manager{
182+
name: "mgr-name",
183+
managercfg: &mgrconfig.Config{
184+
HTTP: testSetverAddrPort,
185+
Cover: true,
186+
},
187+
mgrcfg: &ManagerConfig{
188+
DashboardClient: "test-namespace",
189+
},
190+
}
191+
192+
gcsMock := gcsmocks.NewClient(t)
193+
gotBytes := mockWriteCloser{}
194+
195+
gcsMock.On("FileWriter", test.wantGCSFileName).
196+
Return(&gotBytes, nil).Once()
197+
gcsMock.On("Close").Return().Once()
198+
if test.wantPublish {
199+
gcsMock.On("Publish", test.wantGCSFileName).
200+
Return(nil).Once()
201+
}
202+
ctx := gcs.SetGCSClient(context.Background(), gcsMock)
203+
err := mgr.uploadCoverJSONLToGCS(ctx,
204+
"/teststream&jsonl=1",
205+
"gs://test-bucket",
206+
time.Time{}, test.inputPublish, test.inputCompress, func(w io.Writer, dec *json.Decoder) error {
207+
var v any
208+
if err := dec.Decode(&v); err != nil {
209+
return fmt.Errorf("cover.ProgramCoverage: %w", err)
210+
}
211+
if err := cover.WriteJSLine(w, &v); err != nil {
212+
return fmt.Errorf("cover.WriteJSLine: %w", err)
213+
}
214+
return nil
215+
})
216+
if test.wantError != "" {
217+
assert.Equal(t, test.wantError, err.Error())
218+
} else {
219+
assert.NoError(t, err)
220+
}
221+
assert.Equal(t, 1, gotBytes.closedTimes)
222+
if test.wantCompressed {
223+
gzReader, err := gzip.NewReader(&gotBytes.buf)
224+
assert.NoError(t, err)
225+
defer gzReader.Close()
226+
plainBytes := mockWriteCloser{}
227+
_, err = io.Copy(&plainBytes, gzReader)
228+
assert.NoError(t, err)
229+
gotBytes = plainBytes
230+
}
231+
assert.Equal(t, test.wantGCSFileContent, gotBytes.buf.String())
232+
})
233+
}
234+
}
235+
236+
type mockWriteCloser struct {
237+
buf bytes.Buffer
238+
closedTimes int
239+
}
240+
241+
func (m *mockWriteCloser) Write(p []byte) (n int, err error) {
242+
return m.buf.Write(p)
243+
}
244+
245+
func (m *mockWriteCloser) Close() error {
246+
m.closedTimes++
247+
return nil
248+
}

0 commit comments

Comments
 (0)