go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/vpython/spec/load_test.go (about) 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 spec 16 17 import ( 18 "context" 19 "path/filepath" 20 "runtime" 21 "strings" 22 "testing" 23 24 "google.golang.org/protobuf/encoding/prototext" 25 26 "go.chromium.org/luci/common/testing/testfs" 27 28 "go.chromium.org/luci/vpython/api/vpython" 29 30 . "github.com/smartystreets/goconvey/convey" 31 . "go.chromium.org/luci/common/testing/assertions" 32 ) 33 34 func TestLoadForScript(t *testing.T) { 35 t.Parallel() 36 37 goodSpec := &vpython.Spec{ 38 PythonVersion: "3.4.0", 39 Wheel: []*vpython.Spec_Package{ 40 {Name: "foo/bar", Version: "1"}, 41 {Name: "baz/qux", Version: "2"}, 42 }, 43 } 44 goodSpecData := prototext.Format(goodSpec) 45 badSpecData := "foo: bar" 46 47 l := Loader{ 48 CommonFilesystemBarriers: []string{"BARRIER"}, 49 } 50 51 Convey(`Test LoadForScript`, t, func() { 52 tdir := t.TempDir() 53 c := context.Background() 54 55 // On OSX and Linux the temp dir may be in a symlinked directory (and on OSX 56 // it most often is). LoadForScript expands symlinks, so we need to expand 57 // them here too to make path string comparisons below pass. 58 if runtime.GOOS != "windows" { 59 var err error 60 if tdir, err = filepath.EvalSymlinks(tdir); err != nil { 61 panic(err) 62 } 63 } 64 65 makePath := func(path string) string { 66 return filepath.Join(tdir, filepath.FromSlash(path)) 67 } 68 mustBuild := func(layout map[string]string) { 69 if err := testfs.Build(tdir, layout); err != nil { 70 panic(err) 71 } 72 } 73 74 Convey(`Layout: module with a good spec file`, func() { 75 mustBuild(map[string]string{ 76 "foo/bar/baz/__main__.py": "main", 77 "foo/bar/baz/__init__.py": "", 78 "foo/bar/__init__.py": "", 79 "foo/bar.vpython": goodSpecData, 80 }) 81 spec, path, err := l.LoadForScript(c, makePath("foo/bar/baz"), true) 82 So(err, ShouldBeNil) 83 So(spec, ShouldResembleProto, goodSpec) 84 So(path, ShouldEqual, makePath("foo/bar.vpython")) 85 }) 86 87 Convey(`Layout: module with a bad spec file`, func() { 88 mustBuild(map[string]string{ 89 "foo/bar/baz/__main__.py": "main", 90 "foo/bar/baz/__init__.py": "", 91 "foo/bar/__init__.py": "", 92 "foo/bar.vpython": badSpecData, 93 }) 94 _, _, err := l.LoadForScript(c, makePath("foo/bar/baz"), true) 95 So(err, ShouldErrLike, "failed to unmarshal vpython.Spec") 96 }) 97 98 Convey(`Layout: module with no spec file`, func() { 99 mustBuild(map[string]string{ 100 "foo/bar/baz/__main__.py": "main", 101 "foo/bar/baz/__init__.py": "", 102 "foo/bar/__init__.py": "", 103 "foo/__init__.py": "", 104 }) 105 spec, path, err := l.LoadForScript(c, makePath("foo/bar/baz"), true) 106 So(err, ShouldBeNil) 107 So(spec, ShouldBeNil) 108 So(path, ShouldEqual, "") 109 }) 110 111 Convey(`Layout: individual file with a good spec file`, func() { 112 mustBuild(map[string]string{ 113 "pants.py": "PANTS!", 114 "pants.py.vpython": goodSpecData, 115 }) 116 spec, path, err := l.LoadForScript(c, makePath("pants.py"), false) 117 So(err, ShouldBeNil) 118 So(spec, ShouldResembleProto, goodSpec) 119 So(path, ShouldEqual, makePath("pants.py.vpython")) 120 }) 121 122 Convey(`Layout: individual file with a bad spec file`, func() { 123 mustBuild(map[string]string{ 124 "pants.py": "PANTS!", 125 "pants.py.vpython": badSpecData, 126 }) 127 _, _, err := l.LoadForScript(c, makePath("pants.py"), false) 128 So(err, ShouldErrLike, "failed to unmarshal vpython.Spec") 129 }) 130 131 Convey(`Layout: individual file with no spec (inline or file)`, func() { 132 mustBuild(map[string]string{ 133 "pants.py": "PANTS!", 134 }) 135 spec, path, err := l.LoadForScript(c, makePath("pants.py"), false) 136 So(err, ShouldBeNil) 137 So(spec, ShouldBeNil) 138 So(path, ShouldBeEmpty) 139 }) 140 141 Convey(`Layout: module with good inline spec`, func() { 142 mustBuild(map[string]string{ 143 "foo/bar/baz/__main__.py": strings.Join([]string{ 144 "#!/usr/bin/env vpython", 145 "", 146 "# Test file", 147 "", 148 `"""Docstring`, 149 "[VPYTHON:BEGIN]", 150 goodSpecData, 151 "[VPYTHON:END]", 152 `"""`, 153 "", 154 "# Additional content...", 155 }, "\n"), 156 }) 157 spec, path, err := l.LoadForScript(c, makePath("foo/bar/baz"), true) 158 So(err, ShouldBeNil) 159 So(spec, ShouldResembleProto, goodSpec) 160 So(path, ShouldEqual, makePath("foo/bar/baz/__main__.py")) 161 }) 162 163 Convey(`Layout: individual file with good inline spec`, func() { 164 mustBuild(map[string]string{ 165 "pants.py": strings.Join([]string{ 166 "#!/usr/bin/env vpython", 167 "", 168 "# Test file", 169 "", 170 `"""Docstring`, 171 "[VPYTHON:BEGIN]", 172 goodSpecData, 173 "[VPYTHON:END]", 174 `"""`, 175 "", 176 "# Additional content...", 177 }, "\n"), 178 }) 179 spec, path, err := l.LoadForScript(c, makePath("pants.py"), false) 180 So(err, ShouldBeNil) 181 So(spec, ShouldResembleProto, goodSpec) 182 So(path, ShouldEqual, makePath("pants.py")) 183 }) 184 185 Convey(`Layout: individual file with good inline spec with a prefix`, func() { 186 specParts := strings.Split(goodSpecData, "\n") 187 for i, line := range specParts { 188 specParts[i] = strings.TrimSpace("# " + line) 189 } 190 191 mustBuild(map[string]string{ 192 "pants.py": strings.Join([]string{ 193 "#!/usr/bin/env vpython", 194 "", 195 "# Test file", 196 "#", 197 "# [VPYTHON:BEGIN]", 198 strings.Join(specParts, "\n"), 199 "# [VPYTHON:END]", 200 "", 201 "# Additional content...", 202 }, "\n"), 203 }) 204 205 spec, path, err := l.LoadForScript(c, makePath("pants.py"), false) 206 So(err, ShouldBeNil) 207 So(spec, ShouldResembleProto, goodSpec) 208 So(path, ShouldEqual, makePath("pants.py")) 209 }) 210 211 Convey(`Layout: individual file with bad inline spec`, func() { 212 mustBuild(map[string]string{ 213 "pants.py": strings.Join([]string{ 214 "#!/usr/bin/env vpython", 215 "", 216 "# Test file", 217 "", 218 `"""Docstring`, 219 "[VPYTHON:BEGIN]", 220 badSpecData, 221 "[VPYTHON:END]", 222 `"""`, 223 "", 224 "# Additional content...", 225 }, "\n"), 226 }) 227 228 _, _, err := l.LoadForScript(c, makePath("pants.py"), false) 229 So(err, ShouldErrLike, "failed to unmarshal vpython.Spec") 230 }) 231 232 Convey(`Layout: individual file with inline spec, missing end barrier`, func() { 233 mustBuild(map[string]string{ 234 "pants.py": strings.Join([]string{ 235 "#!/usr/bin/env vpython", 236 "", 237 "# Test file", 238 "", 239 `"""Docstring`, 240 "[VPYTHON:BEGIN]", 241 goodSpecData, 242 `"""`, 243 "", 244 "# Additional content...", 245 }, "\n"), 246 }) 247 248 _, _, err := l.LoadForScript(c, makePath("pants.py"), false) 249 So(err, ShouldErrLike, "unterminated inline spec file") 250 }) 251 252 Convey(`Layout: individual file with a common spec`, func() { 253 mustBuild(map[string]string{ 254 "foo/bar/baz.py": "main", 255 "foo/bar/__init__.py": "", 256 "common.vpython": goodSpecData, 257 }) 258 259 spec, path, err := l.LoadForScript(c, makePath("foo/bar/baz.py"), false) 260 So(err, ShouldBeNil) 261 So(spec, ShouldResembleProto, goodSpec) 262 So(path, ShouldEqual, makePath("common.vpython")) 263 }) 264 265 Convey(`Layout: individual file with a custom common spec`, func() { 266 l.CommonSpecNames = []string{"ohaithere.friend", "common.vpython"} 267 268 mustBuild(map[string]string{ 269 "foo/bar/baz.py": "main", 270 "foo/bar/__init__.py": "", 271 "common.vpython": badSpecData, 272 "ohaithere.friend": goodSpecData, 273 }) 274 275 spec, path, err := l.LoadForScript(c, makePath("foo/bar/baz.py"), false) 276 So(err, ShouldBeNil) 277 So(spec, ShouldResembleProto, goodSpec) 278 So(path, ShouldEqual, makePath("ohaithere.friend")) 279 }) 280 281 Convey(`Layout: individual file with a common spec behind a barrier`, func() { 282 mustBuild(map[string]string{ 283 "foo/bar/baz.py": "main", 284 "foo/bar/__init__.py": "", 285 "foo/BARRIER": "", 286 "common.vpython": goodSpecData, 287 }) 288 289 spec, path, err := l.LoadForScript(c, makePath("foo/bar/baz.py"), false) 290 So(err, ShouldBeNil) 291 So(spec, ShouldBeNil) 292 So(path, ShouldBeEmpty) 293 }) 294 295 Convey(`Layout: individual file with a spec and barrier sharing a folder`, func() { 296 mustBuild(map[string]string{ 297 "foo/bar/baz.py": "main", 298 "foo/bar/__init__.py": "", 299 "BARRIER": "", 300 "common.vpython": goodSpecData, 301 }) 302 303 spec, path, err := l.LoadForScript(c, makePath("foo/bar/baz.py"), false) 304 So(err, ShouldBeNil) 305 So(spec, ShouldResembleProto, goodSpec) 306 So(path, ShouldEqual, makePath("common.vpython")) 307 }) 308 309 Convey(`Layout: module with a common spec`, func() { 310 mustBuild(map[string]string{ 311 "foo/bar/baz/__main__.py": "main", 312 "foo/bar/baz/__init__.py": "", 313 "foo/bar/__init__.py": "", 314 "common.vpython": goodSpecData, 315 }) 316 317 spec, path, err := l.LoadForScript(c, makePath("foo/bar/baz"), true) 318 So(err, ShouldBeNil) 319 So(spec, ShouldResembleProto, goodSpec) 320 So(path, ShouldEqual, makePath("common.vpython")) 321 }) 322 323 Convey(`Layout: individual file with a bad common spec`, func() { 324 mustBuild(map[string]string{ 325 "foo/bar/baz.py": "main", 326 "foo/bar/__init__.py": "", 327 "common.vpython": badSpecData, 328 }) 329 330 _, _, err := l.LoadForScript(c, makePath("foo/bar/baz.py"), false) 331 So(err, ShouldErrLike, "failed to unmarshal vpython.Spec") 332 }) 333 }) 334 }