github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/soong/python/python_test.go (about) 1 // Copyright 2017 Google Inc. All rights reserved. 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 python 16 17 import ( 18 "errors" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "path/filepath" 23 "reflect" 24 "sort" 25 "strings" 26 "testing" 27 28 "android/soong/android" 29 ) 30 31 type pyModule struct { 32 name string 33 actualVersion string 34 pyRunfiles []string 35 depsPyRunfiles []string 36 parSpec string 37 depsParSpecs []string 38 } 39 40 var ( 41 buildNamePrefix = "soong_python_test" 42 moduleVariantErrTemplate = "%s: module %q variant %q: " 43 pkgPathErrTemplate = moduleVariantErrTemplate + 44 "pkg_path: %q must be a relative path contained in par file." 45 badIdentifierErrTemplate = moduleVariantErrTemplate + 46 "srcs: the path %q contains invalid token %q." 47 dupRunfileErrTemplate = moduleVariantErrTemplate + 48 "found two files to be placed at the same runfiles location %q." + 49 " First file: in module %s at path %q." + 50 " Second file: in module %s at path %q." 51 noSrcFileErr = moduleVariantErrTemplate + "doesn't have any source files!" 52 badSrcFileExtErr = moduleVariantErrTemplate + "srcs: found non (.py) file: %q!" 53 badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py) file: %q!" 54 bpFile = "Blueprints" 55 56 data = []struct { 57 desc string 58 mockFiles map[string][]byte 59 60 errors []string 61 expectedBinaries []pyModule 62 }{ 63 { 64 desc: "module without any src files", 65 mockFiles: map[string][]byte{ 66 bpFile: []byte(`subdirs = ["dir"]`), 67 filepath.Join("dir", bpFile): []byte( 68 `python_library_host { 69 name: "lib1", 70 }`, 71 ), 72 }, 73 errors: []string{ 74 fmt.Sprintf(noSrcFileErr, 75 "dir/Blueprints:1:1", "lib1", "PY3"), 76 }, 77 }, 78 { 79 desc: "module with bad src file ext", 80 mockFiles: map[string][]byte{ 81 bpFile: []byte(`subdirs = ["dir"]`), 82 filepath.Join("dir", bpFile): []byte( 83 `python_library_host { 84 name: "lib1", 85 srcs: [ 86 "file1.exe", 87 ], 88 }`, 89 ), 90 "dir/file1.exe": nil, 91 }, 92 errors: []string{ 93 fmt.Sprintf(badSrcFileExtErr, 94 "dir/Blueprints:3:11", "lib1", "PY3", "dir/file1.exe"), 95 }, 96 }, 97 { 98 desc: "module with bad data file ext", 99 mockFiles: map[string][]byte{ 100 bpFile: []byte(`subdirs = ["dir"]`), 101 filepath.Join("dir", bpFile): []byte( 102 `python_library_host { 103 name: "lib1", 104 srcs: [ 105 "file1.py", 106 ], 107 data: [ 108 "file2.py", 109 ], 110 }`, 111 ), 112 "dir/file1.py": nil, 113 "dir/file2.py": nil, 114 }, 115 errors: []string{ 116 fmt.Sprintf(badDataFileExtErr, 117 "dir/Blueprints:6:11", "lib1", "PY3", "dir/file2.py"), 118 }, 119 }, 120 { 121 desc: "module with bad pkg_path format", 122 mockFiles: map[string][]byte{ 123 bpFile: []byte(`subdirs = ["dir"]`), 124 filepath.Join("dir", bpFile): []byte( 125 `python_library_host { 126 name: "lib1", 127 pkg_path: "a/c/../../", 128 srcs: [ 129 "file1.py", 130 ], 131 } 132 133 python_library_host { 134 name: "lib2", 135 pkg_path: "a/c/../../../", 136 srcs: [ 137 "file1.py", 138 ], 139 } 140 141 python_library_host { 142 name: "lib3", 143 pkg_path: "/a/c/../../", 144 srcs: [ 145 "file1.py", 146 ], 147 }`, 148 ), 149 "dir/file1.py": nil, 150 }, 151 errors: []string{ 152 fmt.Sprintf(pkgPathErrTemplate, 153 "dir/Blueprints:11:15", "lib2", "PY3", "a/c/../../../"), 154 fmt.Sprintf(pkgPathErrTemplate, 155 "dir/Blueprints:19:15", "lib3", "PY3", "/a/c/../../"), 156 }, 157 }, 158 { 159 desc: "module with bad runfile src path format", 160 mockFiles: map[string][]byte{ 161 bpFile: []byte(`subdirs = ["dir"]`), 162 filepath.Join("dir", bpFile): []byte( 163 `python_library_host { 164 name: "lib1", 165 pkg_path: "a/b/c/", 166 srcs: [ 167 ".file1.py", 168 "123/file1.py", 169 "-e/f/file1.py", 170 ], 171 }`, 172 ), 173 "dir/.file1.py": nil, 174 "dir/123/file1.py": nil, 175 "dir/-e/f/file1.py": nil, 176 }, 177 errors: []string{ 178 fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11", 179 "lib1", "PY3", "runfiles/a/b/c/-e/f/file1.py", "-e"), 180 fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11", 181 "lib1", "PY3", "runfiles/a/b/c/.file1.py", ".file1"), 182 fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11", 183 "lib1", "PY3", "runfiles/a/b/c/123/file1.py", "123"), 184 }, 185 }, 186 { 187 desc: "module with duplicate runfile path", 188 mockFiles: map[string][]byte{ 189 bpFile: []byte(`subdirs = ["dir"]`), 190 filepath.Join("dir", bpFile): []byte( 191 `python_library_host { 192 name: "lib1", 193 pkg_path: "a/b/", 194 srcs: [ 195 "c/file1.py", 196 ], 197 } 198 199 python_library_host { 200 name: "lib2", 201 pkg_path: "a/b/c/", 202 srcs: [ 203 "file1.py", 204 ], 205 libs: [ 206 "lib1", 207 ], 208 } 209 `, 210 ), 211 "dir/c/file1.py": nil, 212 "dir/file1.py": nil, 213 }, 214 errors: []string{ 215 fmt.Sprintf(dupRunfileErrTemplate, "dir/Blueprints:9:6", 216 "lib2", "PY3", "runfiles/a/b/c/file1.py", "lib2", "dir/file1.py", 217 "lib1", "dir/c/file1.py"), 218 }, 219 }, 220 { 221 desc: "module for testing dependencies", 222 mockFiles: map[string][]byte{ 223 bpFile: []byte(`subdirs = ["dir"]`), 224 filepath.Join("dir", bpFile): []byte( 225 `python_defaults { 226 name: "default_lib", 227 srcs: [ 228 "default.py", 229 ], 230 version: { 231 py2: { 232 enabled: true, 233 srcs: [ 234 "default_py2.py", 235 ], 236 }, 237 py3: { 238 enabled: false, 239 srcs: [ 240 "default_py3.py", 241 ], 242 }, 243 }, 244 } 245 246 python_library_host { 247 name: "lib5", 248 pkg_path: "a/b/", 249 srcs: [ 250 "file1.py", 251 ], 252 version: { 253 py2: { 254 enabled: true, 255 }, 256 py3: { 257 enabled: true, 258 }, 259 }, 260 } 261 262 python_library_host { 263 name: "lib6", 264 pkg_path: "c/d/", 265 srcs: [ 266 "file2.py", 267 ], 268 libs: [ 269 "lib5", 270 ], 271 } 272 273 python_binary_host { 274 name: "bin", 275 defaults: ["default_lib"], 276 pkg_path: "e/", 277 srcs: [ 278 "bin.py", 279 ], 280 libs: [ 281 "lib5", 282 ], 283 version: { 284 py3: { 285 enabled: true, 286 srcs: [ 287 "file4.py", 288 ], 289 libs: [ 290 "lib6", 291 ], 292 }, 293 }, 294 }`, 295 ), 296 filepath.Join("dir", "default.py"): nil, 297 filepath.Join("dir", "default_py2.py"): nil, 298 filepath.Join("dir", "default_py3.py"): nil, 299 filepath.Join("dir", "file1.py"): nil, 300 filepath.Join("dir", "file2.py"): nil, 301 filepath.Join("dir", "bin.py"): nil, 302 filepath.Join("dir", "file4.py"): nil, 303 stubTemplateHost: []byte(`PYTHON_BINARY = '%interpreter%' 304 MAIN_FILE = '%main%'`), 305 }, 306 expectedBinaries: []pyModule{ 307 { 308 name: "bin", 309 actualVersion: "PY3", 310 pyRunfiles: []string{ 311 "runfiles/e/default.py", 312 "runfiles/e/bin.py", 313 "runfiles/e/default_py3.py", 314 "runfiles/e/file4.py", 315 }, 316 depsPyRunfiles: []string{ 317 "runfiles/a/b/file1.py", 318 "runfiles/c/d/file2.py", 319 }, 320 parSpec: "-P runfiles/e -C dir/ -l @prefix@/.intermediates/dir/bin/PY3/dir_.list", 321 depsParSpecs: []string{ 322 "-P runfiles/a/b -C dir/ -l @prefix@/.intermediates/dir/lib5/PY3/dir_.list", 323 "-P runfiles/c/d -C dir/ -l @prefix@/.intermediates/dir/lib6/PY3/dir_.list", 324 }, 325 }, 326 }, 327 }, 328 } 329 ) 330 331 func TestPythonModule(t *testing.T) { 332 config, buildDir := setupBuildEnv(t) 333 defer tearDownBuildEnv(buildDir) 334 for _, d := range data { 335 t.Run(d.desc, func(t *testing.T) { 336 ctx := android.NewTestContext() 337 ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { 338 ctx.BottomUp("version_split", versionSplitMutator()).Parallel() 339 }) 340 ctx.RegisterModuleType("python_library_host", 341 android.ModuleFactoryAdaptor(PythonLibraryHostFactory)) 342 ctx.RegisterModuleType("python_binary_host", 343 android.ModuleFactoryAdaptor(PythonBinaryHostFactory)) 344 ctx.RegisterModuleType("python_defaults", 345 android.ModuleFactoryAdaptor(defaultsFactory)) 346 ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) 347 ctx.Register() 348 ctx.MockFileSystem(d.mockFiles) 349 _, testErrs := ctx.ParseBlueprintsFiles(bpFile) 350 android.FailIfErrored(t, testErrs) 351 _, actErrs := ctx.PrepareBuildActions(config) 352 if len(actErrs) > 0 { 353 testErrs = append(testErrs, expectErrors(t, actErrs, d.errors)...) 354 } else { 355 for _, e := range d.expectedBinaries { 356 testErrs = append(testErrs, 357 expectModule(t, ctx, buildDir, e.name, 358 e.actualVersion, 359 e.pyRunfiles, e.depsPyRunfiles, 360 e.parSpec, e.depsParSpecs)...) 361 } 362 } 363 android.FailIfErrored(t, testErrs) 364 }) 365 } 366 } 367 368 func expectErrors(t *testing.T, actErrs []error, expErrs []string) (testErrs []error) { 369 actErrStrs := []string{} 370 for _, v := range actErrs { 371 actErrStrs = append(actErrStrs, v.Error()) 372 } 373 sort.Strings(actErrStrs) 374 if len(actErrStrs) != len(expErrs) { 375 t.Errorf("got (%d) errors, expected (%d) errors!", len(actErrStrs), len(expErrs)) 376 for _, v := range actErrStrs { 377 testErrs = append(testErrs, errors.New(v)) 378 } 379 } else { 380 sort.Strings(expErrs) 381 for i, v := range actErrStrs { 382 if v != expErrs[i] { 383 testErrs = append(testErrs, errors.New(v)) 384 } 385 } 386 } 387 388 return 389 } 390 391 func expectModule(t *testing.T, ctx *android.TestContext, buildDir, name, variant string, 392 expPyRunfiles, expDepsPyRunfiles []string, 393 expParSpec string, expDepsParSpecs []string) (testErrs []error) { 394 module := ctx.ModuleForTests(name, variant) 395 396 base, baseOk := module.Module().(*Module) 397 if !baseOk { 398 t.Fatalf("%s is not Python module!", name) 399 } 400 401 actPyRunfiles := []string{} 402 for _, path := range base.srcsPathMappings { 403 actPyRunfiles = append(actPyRunfiles, path.dest) 404 } 405 406 if !reflect.DeepEqual(actPyRunfiles, expPyRunfiles) { 407 testErrs = append(testErrs, errors.New(fmt.Sprintf( 408 `binary "%s" variant "%s" has unexpected pyRunfiles: %q!`, 409 base.Name(), 410 base.properties.Actual_version, 411 actPyRunfiles))) 412 } 413 414 if !reflect.DeepEqual(base.depsPyRunfiles, expDepsPyRunfiles) { 415 testErrs = append(testErrs, errors.New(fmt.Sprintf( 416 `binary "%s" variant "%s" has unexpected depsPyRunfiles: %q!`, 417 base.Name(), 418 base.properties.Actual_version, 419 base.depsPyRunfiles))) 420 } 421 422 if base.parSpec.soongParArgs() != strings.Replace(expParSpec, "@prefix@", buildDir, 1) { 423 testErrs = append(testErrs, errors.New(fmt.Sprintf( 424 `binary "%s" variant "%s" has unexpected parSpec: %q!`, 425 base.Name(), 426 base.properties.Actual_version, 427 base.parSpec.soongParArgs()))) 428 } 429 430 actDepsParSpecs := []string{} 431 for i, p := range base.depsParSpecs { 432 actDepsParSpecs = append(actDepsParSpecs, p.soongParArgs()) 433 expDepsParSpecs[i] = strings.Replace(expDepsParSpecs[i], "@prefix@", buildDir, 1) 434 } 435 436 if !reflect.DeepEqual(actDepsParSpecs, expDepsParSpecs) { 437 testErrs = append(testErrs, errors.New(fmt.Sprintf( 438 `binary "%s" variant "%s" has unexpected depsParSpecs: %q!`, 439 base.Name(), 440 base.properties.Actual_version, 441 actDepsParSpecs))) 442 } 443 444 return 445 } 446 447 func setupBuildEnv(t *testing.T) (config android.Config, buildDir string) { 448 buildDir, err := ioutil.TempDir("", buildNamePrefix) 449 if err != nil { 450 t.Fatal(err) 451 } 452 453 config = android.TestConfig(buildDir, nil) 454 455 return 456 } 457 458 func tearDownBuildEnv(buildDir string) { 459 os.RemoveAll(buildDir) 460 }