OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package cipd | |
6 | |
7 import ( | |
8 "archive/zip" | |
9 "bytes" | |
10 "crypto/sha1" | |
11 "encoding/hex" | |
12 "fmt" | |
13 "io" | |
14 "io/ioutil" | |
15 "os" | |
16 "runtime" | |
17 "testing" | |
18 | |
19 . "github.com/smartystreets/goconvey/convey" | |
20 ) | |
21 | |
22 func TestGoVersion(t *testing.T) { | |
23 Convey("Make sure using pinned Go version", t, func() { | |
24 // Change this when rolling pinned Go version. Some tests here m
ay depend | |
25 // on zlib implementation details compiled in Go stdlib. | |
26 So(runtime.Version(), ShouldEqual, "go1.4") | |
27 }) | |
28 } | |
29 | |
30 func TestBuildInstance(t *testing.T) { | |
31 const goodManifest = `{ | |
32 "format_version": "1", | |
33 "package_name": "testing" | |
34 }` | |
35 | |
36 Convey("Building empty package", t, func() { | |
37 out := bytes.Buffer{} | |
38 err := BuildInstance(BuildInstanceOptions{ | |
39 Input: []File{}, | |
40 Output: &out, | |
41 PackageName: "testing", | |
42 }) | |
43 So(err, ShouldBeNil) | |
44 | |
45 // BuildInstance builds deterministic zip. It MUST NOT depend on | |
46 // the platform, or a time of day, or anything else, only on the
input data. | |
47 So(getSHA1(&out), ShouldEqual, "23f2c4900785ac8faa2f38e473925b84
0e574ccc") | |
48 | |
49 // There should be a single file: the manifest. | |
50 files := readZip(out.Bytes()) | |
51 So(files, ShouldResemble, []zippedFile{ | |
52 zippedFile{ | |
53 // See structs.go, manifestName. | |
54 name: ".cipdpkg/manifest.json", | |
55 size: uint64(len(goodManifest)), | |
56 mode: 0600, | |
57 body: []byte(goodManifest), | |
58 }, | |
59 }) | |
60 }) | |
61 | |
62 Convey("Building package with a bunch of files", t, func() { | |
63 out := bytes.Buffer{} | |
64 err := BuildInstance(BuildInstanceOptions{ | |
65 Input: []File{ | |
66 makeTestFile("testing/qwerty", "12345", false), | |
67 makeTestFile("abc", "duh", true), | |
68 makeTestSymlink("rel_symlink", "abc"), | |
69 makeTestSymlink("abs_symlink", "/abc/def"), | |
70 }, | |
71 Output: &out, | |
72 PackageName: "testing", | |
73 }) | |
74 So(err, ShouldBeNil) | |
75 | |
76 // The manifest and all added files. | |
77 files := readZip(out.Bytes()) | |
78 So(files, ShouldResemble, []zippedFile{ | |
79 zippedFile{ | |
80 name: "testing/qwerty", | |
81 size: 5, | |
82 mode: 0600, | |
83 body: []byte("12345"), | |
84 }, | |
85 zippedFile{ | |
86 name: "abc", | |
87 size: 3, | |
88 mode: 0700, | |
89 body: []byte("duh"), | |
90 }, | |
91 zippedFile{ | |
92 name: "rel_symlink", | |
93 size: 3, | |
94 mode: 0600 | os.ModeSymlink, | |
95 body: []byte("abc"), | |
96 }, | |
97 zippedFile{ | |
98 name: "abs_symlink", | |
99 size: 8, | |
100 mode: 0600 | os.ModeSymlink, | |
101 body: []byte("/abc/def"), | |
102 }, | |
103 zippedFile{ | |
104 // See structs.go, manifestName. | |
105 name: ".cipdpkg/manifest.json", | |
106 size: uint64(len(goodManifest)), | |
107 mode: 0600, | |
108 body: []byte(goodManifest), | |
109 }, | |
110 }) | |
111 }) | |
112 | |
113 Convey("Duplicate files fail", t, func() { | |
114 err := BuildInstance(BuildInstanceOptions{ | |
115 Input: []File{ | |
116 makeTestFile("a", "12345", false), | |
117 makeTestFile("a", "12345", false), | |
118 }, | |
119 Output: &bytes.Buffer{}, | |
120 PackageName: "testing", | |
121 }) | |
122 So(err, ShouldNotBeNil) | |
123 }) | |
124 | |
125 Convey("Writing to service dir fails", t, func() { | |
126 err := BuildInstance(BuildInstanceOptions{ | |
127 Input: []File{ | |
128 makeTestFile(".cipdpkg/stuff", "12345", false), | |
129 }, | |
130 Output: &bytes.Buffer{}, | |
131 PackageName: "testing", | |
132 }) | |
133 So(err, ShouldNotBeNil) | |
134 }) | |
135 | |
136 Convey("Bad name fails", t, func() { | |
137 err := BuildInstance(BuildInstanceOptions{ | |
138 Output: &bytes.Buffer{}, | |
139 PackageName: "../../asdad", | |
140 }) | |
141 So(err, ShouldNotBeNil) | |
142 }) | |
143 | |
144 } | |
145 | |
146 //////////////////////////////////////////////////////////////////////////////// | |
147 | |
148 // getSHA1 returns SHA1 hex digest of a byte buffer. | |
149 func getSHA1(buf *bytes.Buffer) string { | |
150 h := sha1.New() | |
151 h.Write(buf.Bytes()) | |
152 return hex.EncodeToString(h.Sum(nil)) | |
153 } | |
154 | |
155 //////////////////////////////////////////////////////////////////////////////// | |
156 | |
157 type zippedFile struct { | |
158 name string | |
159 size uint64 | |
160 mode os.FileMode | |
161 body []byte | |
162 } | |
163 | |
164 // readZip scans zip directory and returns files it finds. | |
165 func readZip(data []byte) []zippedFile { | |
166 z, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) | |
167 if err != nil { | |
168 panic("Failed to open zip file") | |
169 } | |
170 files := make([]zippedFile, len(z.File)) | |
171 for i, zf := range z.File { | |
172 reader, err := zf.Open() | |
173 if err != nil { | |
174 panic("Failed to open file inside zip") | |
175 } | |
176 body, err := ioutil.ReadAll(reader) | |
177 if err != nil { | |
178 panic("Failed to read zipped file") | |
179 } | |
180 files[i] = zippedFile{ | |
181 name: zf.Name, | |
182 size: zf.FileHeader.UncompressedSize64, | |
183 mode: zf.Mode(), | |
184 body: body, | |
185 } | |
186 } | |
187 return files | |
188 } | |
189 | |
190 //////////////////////////////////////////////////////////////////////////////// | |
191 | |
192 type testFile struct { | |
193 name string | |
194 data string | |
195 executable bool | |
196 symlinkTarget string | |
197 } | |
198 | |
199 func (f *testFile) Name() string { return f.name } | |
200 func (f *testFile) Size() uint64 { return uint64(len(f.data)) } | |
201 func (f *testFile) Executable() bool { return f.executable } | |
202 func (f *testFile) Symlink() bool { return f.symlinkTarget != "" } | |
203 | |
204 func (f *testFile) SymlinkTarget() (string, error) { | |
205 if f.symlinkTarget == "" { | |
206 return "", fmt.Errorf("Not a symlink: %s", f.Name()) | |
207 } | |
208 return f.symlinkTarget, nil | |
209 } | |
210 | |
211 func (f *testFile) Open() (io.ReadCloser, error) { | |
212 if f.Symlink() { | |
213 return nil, fmt.Errorf("Can't open symlink: %s", f.Name()) | |
214 } | |
215 r := bytes.NewReader([]byte(f.data)) | |
216 return ioutil.NopCloser(r), nil | |
217 } | |
218 | |
219 func makeTestFile(name string, data string, executable bool) File { | |
220 return &testFile{ | |
221 name: name, | |
222 data: data, | |
223 executable: executable, | |
224 } | |
225 } | |
226 | |
227 func makeTestSymlink(name string, target string) File { | |
228 return &testFile{ | |
229 name: name, | |
230 symlinkTarget: target, | |
231 } | |
232 } | |
OLD | NEW |