| 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 |