Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(506)

Side by Side Diff: client/cmd/isolate/upload_tracker_test.go

Issue 2992693002: isolate: add uploadtracker tests for symlinks and isolated files (Closed)
Patch Set: address review comments Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « client/cmd/isolate/upload_tracker.go ('k') | client/cmd/isolate/uploader.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2017 The LUCI Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package main
16
17 import (
18 "bytes"
19 "fmt"
20 "io"
21 "os"
22 "reflect"
23 "testing"
24
25 "github.com/luci/luci-go/common/isolated"
26 "github.com/luci/luci-go/common/isolatedclient"
27 . "github.com/smartystreets/goconvey/convey"
28 )
29
30 // Fake OS imitiates a filesystem by storing file contents in a map.
31 // It also provides a dummy Readlink implementation.
32 type fakeOS struct {
33 files map[string]*bytes.Buffer
34 }
35
36 // shouldResembleByteMap asserts that actual (a map[string]*bytes.Buffer) contai ns
37 // the same data as expected (a map[string][]byte).
38 // The types of actual and expected differ to make writing tests with fakeOS mor e convenient.
39 func shouldResembleByteMap(actual interface{}, expected ...interface{}) string {
40 act, ok := actual.(map[string]*bytes.Buffer)
41 if !ok {
42 return "actual is not a map[string]*bytes.Buffer"
43 }
44 if len(expected) != 1 {
45 return "expected is not a map[string][]byte"
46 }
47 exp, ok := expected[0].(map[string][]byte)
48 if !ok {
49 return "expected is not a map[string][]byte"
50 }
51
52 if len(act) != len(exp) {
53 return fmt.Sprintf("len(actual) != len(expected): %v != %v", len (act), len(exp))
54 }
55
56 for k, v := range act {
57 if got, want := v.Bytes(), exp[k]; !reflect.DeepEqual(got, want) {
58 return fmt.Sprintf("actual[%q] != expected[%q]: %q != %q ", k, k, got, want)
59 }
60 }
61 return ""
62 }
63
64 func (fos *fakeOS) Readlink(name string) (string, error) {
65 return "link:" + name, nil
66 }
67
68 type nopWriteCloser struct {
69 io.Writer
70 }
71
72 func (nopWriteCloser) Close() error { return nil }
73
74 // implements OpenFile by returning a writer that writes to a bytes.Buffer.
75 func (fos *fakeOS) OpenFile(name string, flag int, perm os.FileMode) (io.WriteCl oser, error) {
76 if fos.files == nil {
77 fos.files = make(map[string]*bytes.Buffer)
78 }
79 fos.files[name] = &bytes.Buffer{}
80 return nopWriteCloser{fos.files[name]}, nil
81 }
82
83 // fakeChecker implements Checker by responding to method invocations by
84 // invoking the supplied callback with the supplied item, and a hard-coded *Push State.
85 type fakeChecker struct {
86 ps *isolatedclient.PushState
87 }
88
89 type checkerAddItemArgs struct {
90 item *Item
91 isolated bool
92 }
93
94 type checkerAddItemResponse struct {
95 item *Item
96 ps *isolatedclient.PushState
97 }
98
99 func (checker *fakeChecker) AddItem(item *Item, isolated bool, callback CheckerC allback) {
100 callback(item, checker.ps)
101 }
102
103 func (checker *fakeChecker) Close() error { return nil }
104
105 // fakeChecker implements Uploader while recording method arguments.
106 type fakeUploader struct {
107 // uploadBytesCalls is a record of the arguments to each call of UploadB ytes.
108 uploadBytesCalls []uploaderUploadBytesArgs
109
110 Uploader // TODO(mcgreevy): implement other methods.
111 }
112
113 type uploaderUploadBytesArgs struct {
114 relPath string
115 isolJSON []byte
116 ps *isolatedclient.PushState
117 }
118
119 func (uploader *fakeUploader) UploadBytes(relPath string, isolJSON []byte, ps *i solatedclient.PushState, f func()) {
120 uploader.uploadBytesCalls = append(uploader.uploadBytesCalls, uploaderUp loadBytesArgs{relPath, isolJSON, ps})
121 }
122
123 func (uploader *fakeUploader) Close() error { return nil }
124
125 func TestSkipsUpload(t *testing.T) {
126 t.Parallel()
127 Convey(`nil push state signals that upload should be skipped`, t, func() {
128 isol := &isolated.Isolated{}
129
130 // nil PushState means skip the upload.
131 checker := &fakeChecker{ps: nil}
132
133 uploader := &fakeUploader{}
134
135 ut := NewUploadTracker(checker, uploader, isol)
136 fos := &fakeOS{}
137 ut.lOS = fos // Override filesystem calls with fake.
138
139 parts := partitionedDeps{} // no actual deps.
140 err := ut.UploadDeps(parts)
141 So(err, ShouldBeNil)
142
143 // No deps, so Files should be empty.
144 wantFiles := map[string]isolated.File{}
145 So(ut.isol.Files, ShouldResemble, wantFiles)
146
147 isolSummary, err := ut.Finalize("/a/isolatedPath")
148 So(err, ShouldBeNil)
149
150 // In this test, the only item that is checked and uploaded is t he generated isolated file.
151 wantIsolJSON := []byte(`{"algo":"","version":""}`)
152 So(fos.files, shouldResembleByteMap, map[string][]byte{"/a/isola tedPath": wantIsolJSON})
153
154 So(isolSummary.Digest, ShouldResemble, isolated.HashBytes(wantIs olJSON))
155 So(isolSummary.Name, ShouldEqual, "isolatedPath")
156
157 So(uploader.uploadBytesCalls, ShouldEqual, nil)
158 })
159 }
160
161 func TestDontSkipUpload(t *testing.T) {
162 t.Parallel()
163 Convey(`passing non-nil push state signals that upload should be perform ed`, t, func() {
164 isol := &isolated.Isolated{}
165
166 // non-nil PushState means don't skip the upload.
167 checker := &fakeChecker{ps: &isolatedclient.PushState{}}
168
169 uploader := &fakeUploader{}
170
171 ut := NewUploadTracker(checker, uploader, isol)
172 fos := &fakeOS{}
173 ut.lOS = fos // Override filesystem calls with fake.
174
175 parts := partitionedDeps{} // no actual deps.
176 err := ut.UploadDeps(parts)
177 So(err, ShouldBeNil)
178
179 // No deps, so Files should be empty.
180 wantFiles := map[string]isolated.File{}
181 So(ut.isol.Files, ShouldResemble, wantFiles)
182
183 isolSummary, err := ut.Finalize("/a/isolatedPath")
184 So(err, ShouldBeNil)
185
186 // In this test, the only item that is checked and uploaded is t he generated isolated file.
187 wantIsolJSON := []byte(`{"algo":"","version":""}`)
188 So(fos.files, shouldResembleByteMap, map[string][]byte{"/a/isola tedPath": wantIsolJSON})
189
190 So(isolSummary.Digest, ShouldResemble, isolated.HashBytes(wantIs olJSON))
191 So(isolSummary.Name, ShouldEqual, "isolatedPath")
192
193 // Upload was not skipped.
194 So(uploader.uploadBytesCalls, ShouldResemble, []uploaderUploadBy tesArgs{
195 {"isolatedPath", wantIsolJSON, checker.ps},
196 })
197 })
198 }
199
200 func TestHandlesSymlinks(t *testing.T) {
201 t.Parallel()
202 Convey(`Symlinks should be stored in the isolated json`, t, func() {
203 isol := &isolated.Isolated{}
204
205 // The checker and uploader will only be used for uploading the isolated JSON.
206 // The symlinks themselves do not need to be checked or uploaded .
207 // non-nil PushState means don't skip the upload.
208 checker := &fakeChecker{ps: &isolatedclient.PushState{}}
209 uploader := &fakeUploader{}
210
211 ut := NewUploadTracker(checker, uploader, isol)
212 fos := &fakeOS{}
213 ut.lOS = fos // Override filesystem calls with fake.
214
215 parts := partitionedDeps{
216 links: itemGroup{
217 items: []*Item{
218 {Path: "/a/b/c", RelPath: "c"},
219 },
220 totalSize: 9,
221 },
222 }
223 err := ut.UploadDeps(parts)
224 So(err, ShouldBeNil)
225
226 path := "link:/a/b/c"
227 wantFiles := map[string]isolated.File{"c": {Link: &path}}
228
229 So(ut.isol.Files, ShouldResemble, wantFiles)
230
231 // Symlinks are not uploaded.
232 So(uploader.uploadBytesCalls, ShouldEqual, nil)
233
234 isolSummary, err := ut.Finalize("/a/isolatedPath")
235 So(err, ShouldBeNil)
236
237 // In this test, the only item that is checked and uploaded is t he generated isolated file.
238 wantIsolJSON := []byte(`{"algo":"","files":{"c":{"l":"link:/a/b/ c"}},"version":""}`)
239 So(fos.files, shouldResembleByteMap, map[string][]byte{"/a/isola tedPath": wantIsolJSON})
240
241 So(isolSummary.Digest, ShouldResemble, isolated.HashBytes(wantIs olJSON))
242 So(isolSummary.Name, ShouldEqual, "isolatedPath")
243
244 So(uploader.uploadBytesCalls, ShouldResemble, []uploaderUploadBy tesArgs{
245 {"isolatedPath", wantIsolJSON, checker.ps},
246 })
247 })
248 }
OLDNEW
« no previous file with comments | « client/cmd/isolate/upload_tracker.go ('k') | client/cmd/isolate/uploader.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698