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 15package python 16 17import ( 18 "fmt" 19 "os" 20 "path/filepath" 21 "testing" 22 23 "android/soong/android" 24 "android/soong/cc" 25) 26 27type pyModule struct { 28 name string 29 actualVersion string 30 pyRunfiles []string 31 srcsZip string 32 depsSrcsZips []string 33} 34 35var ( 36 buildNamePrefix = "soong_python_test" 37 // We allow maching almost anything before the actual variant so that the os/arch variant 38 // is matched. 39 moduleVariantErrTemplate = `%s: module %q variant "[a-zA-Z0-9_]*%s": ` 40 pkgPathErrTemplate = moduleVariantErrTemplate + 41 "pkg_path: %q must be a relative path contained in par file." 42 badIdentifierErrTemplate = moduleVariantErrTemplate + 43 "srcs: the path %q contains invalid subpath %q." 44 dupRunfileErrTemplate = moduleVariantErrTemplate + 45 "found two files to be placed at the same location within zip %q." + 46 " First file: in module %s at path %q." + 47 " Second file: in module %s at path %q." 48 noSrcFileErr = moduleVariantErrTemplate + "doesn't have any source files!" 49 badSrcFileExtErr = moduleVariantErrTemplate + "srcs: found non (.py|.proto) file: %q!" 50 badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py|.proto) file: %q!" 51 bpFile = "Android.bp" 52 53 data = []struct { 54 desc string 55 mockFiles android.MockFS 56 57 errors []string 58 expectedBinaries []pyModule 59 }{ 60 { 61 desc: "module without any src files", 62 mockFiles: map[string][]byte{ 63 filepath.Join("dir", bpFile): []byte( 64 `python_library_host { 65 name: "lib1", 66 }`, 67 ), 68 }, 69 errors: []string{ 70 fmt.Sprintf(noSrcFileErr, 71 "dir/Android.bp:1:1", "lib1", "PY3"), 72 }, 73 }, 74 { 75 desc: "module with bad src file ext", 76 mockFiles: map[string][]byte{ 77 filepath.Join("dir", bpFile): []byte( 78 `python_library_host { 79 name: "lib1", 80 srcs: [ 81 "file1.exe", 82 ], 83 }`, 84 ), 85 "dir/file1.exe": nil, 86 }, 87 errors: []string{ 88 fmt.Sprintf(badSrcFileExtErr, 89 "dir/Android.bp:3:11", "lib1", "PY3", "dir/file1.exe"), 90 }, 91 }, 92 { 93 desc: "module with bad data file ext", 94 mockFiles: map[string][]byte{ 95 filepath.Join("dir", bpFile): []byte( 96 `python_library_host { 97 name: "lib1", 98 srcs: [ 99 "file1.py", 100 ], 101 data: [ 102 "file2.py", 103 ], 104 }`, 105 ), 106 "dir/file1.py": nil, 107 "dir/file2.py": nil, 108 }, 109 errors: []string{ 110 fmt.Sprintf(badDataFileExtErr, 111 "dir/Android.bp:6:11", "lib1", "PY3", "dir/file2.py"), 112 }, 113 }, 114 { 115 desc: "module with bad pkg_path format", 116 mockFiles: map[string][]byte{ 117 filepath.Join("dir", bpFile): []byte( 118 `python_library_host { 119 name: "lib1", 120 pkg_path: "a/c/../../", 121 srcs: [ 122 "file1.py", 123 ], 124 } 125 126 python_library_host { 127 name: "lib2", 128 pkg_path: "a/c/../../../", 129 srcs: [ 130 "file1.py", 131 ], 132 } 133 134 python_library_host { 135 name: "lib3", 136 pkg_path: "/a/c/../../", 137 srcs: [ 138 "file1.py", 139 ], 140 }`, 141 ), 142 "dir/file1.py": nil, 143 }, 144 errors: []string{ 145 fmt.Sprintf(pkgPathErrTemplate, 146 "dir/Android.bp:11:15", "lib2", "PY3", "a/c/../../../"), 147 fmt.Sprintf(pkgPathErrTemplate, 148 "dir/Android.bp:19:15", "lib3", "PY3", "/a/c/../../"), 149 }, 150 }, 151 { 152 desc: "module with bad runfile src path format", 153 mockFiles: map[string][]byte{ 154 filepath.Join("dir", bpFile): []byte( 155 `python_library_host { 156 name: "lib1", 157 pkg_path: "a/b/c/", 158 srcs: [ 159 ".file1.py", 160 "123/file1.py", 161 "-e/f/file1.py", 162 ], 163 }`, 164 ), 165 "dir/.file1.py": nil, 166 "dir/123/file1.py": nil, 167 "dir/-e/f/file1.py": nil, 168 }, 169 errors: []string{ 170 fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11", 171 "lib1", "PY3", "a/b/c/-e/f/file1.py", "-e"), 172 fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11", 173 "lib1", "PY3", "a/b/c/.file1.py", ".file1"), 174 fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11", 175 "lib1", "PY3", "a/b/c/123/file1.py", "123"), 176 }, 177 }, 178 { 179 desc: "module with duplicate runfile path", 180 mockFiles: map[string][]byte{ 181 filepath.Join("dir", bpFile): []byte( 182 `python_library_host { 183 name: "lib1", 184 pkg_path: "a/b/", 185 srcs: [ 186 "c/file1.py", 187 ], 188 } 189 190 python_library_host { 191 name: "lib2", 192 pkg_path: "a/b/c/", 193 srcs: [ 194 "file1.py", 195 ], 196 libs: [ 197 "lib1", 198 ], 199 } 200 201 python_binary_host { 202 name: "bin", 203 pkg_path: "e/", 204 srcs: [ 205 "bin.py", 206 ], 207 libs: [ 208 "lib2", 209 ], 210 } 211 `, 212 ), 213 "dir/c/file1.py": nil, 214 "dir/file1.py": nil, 215 "dir/bin.py": nil, 216 }, 217 errors: []string{ 218 fmt.Sprintf(dupRunfileErrTemplate, "dir/Android.bp:20:6", 219 "bin", "PY3", "a/b/c/file1.py", "bin", "dir/file1.py", 220 "lib1", "dir/c/file1.py"), 221 }, 222 }, 223 { 224 desc: "module for testing dependencies", 225 mockFiles: map[string][]byte{ 226 filepath.Join("dir", bpFile): []byte( 227 `python_defaults { 228 name: "default_lib", 229 srcs: [ 230 "default.py", 231 ], 232 version: { 233 py2: { 234 enabled: true, 235 srcs: [ 236 "default_py2.py", 237 ], 238 }, 239 py3: { 240 enabled: false, 241 srcs: [ 242 "default_py3.py", 243 ], 244 }, 245 }, 246 } 247 248 python_library_host { 249 name: "lib5", 250 pkg_path: "a/b/", 251 srcs: [ 252 "file1.py", 253 ], 254 version: { 255 py2: { 256 enabled: true, 257 }, 258 py3: { 259 enabled: true, 260 }, 261 }, 262 } 263 264 python_library_host { 265 name: "lib6", 266 pkg_path: "c/d/", 267 srcs: [ 268 "file2.py", 269 ], 270 libs: [ 271 "lib5", 272 ], 273 } 274 275 python_binary_host { 276 name: "bin", 277 defaults: ["default_lib"], 278 pkg_path: "e/", 279 srcs: [ 280 "bin.py", 281 ], 282 libs: [ 283 "lib5", 284 ], 285 version: { 286 py3: { 287 enabled: true, 288 srcs: [ 289 "file4.py", 290 ], 291 libs: [ 292 "lib6", 293 ], 294 }, 295 }, 296 }`, 297 ), 298 filepath.Join("dir", "default.py"): nil, 299 filepath.Join("dir", "default_py2.py"): nil, 300 filepath.Join("dir", "default_py3.py"): nil, 301 filepath.Join("dir", "file1.py"): nil, 302 filepath.Join("dir", "file2.py"): nil, 303 filepath.Join("dir", "bin.py"): nil, 304 filepath.Join("dir", "file4.py"): nil, 305 }, 306 expectedBinaries: []pyModule{ 307 { 308 name: "bin", 309 actualVersion: "PY3", 310 pyRunfiles: []string{ 311 "e/default.py", 312 "e/bin.py", 313 "e/default_py3.py", 314 "e/file4.py", 315 }, 316 srcsZip: "out/soong/.intermediates/dir/bin/PY3/bin.py.srcszip", 317 }, 318 }, 319 }, 320 } 321) 322 323func TestPythonModule(t *testing.T) { 324 for _, d := range data { 325 if d.desc != "module with duplicate runfile path" { 326 continue 327 } 328 d.mockFiles[filepath.Join("common", bpFile)] = []byte(` 329python_library { 330 name: "py3-stdlib", 331 host_supported: true, 332} 333cc_binary { 334 name: "py3-launcher", 335 host_supported: true, 336} 337`) 338 339 t.Run(d.desc, func(t *testing.T) { 340 result := android.GroupFixturePreparers( 341 android.PrepareForTestWithDefaults, 342 android.PrepareForTestWithArchMutator, 343 android.PrepareForTestWithAllowMissingDependencies, 344 cc.PrepareForTestWithCcDefaultModules, 345 PrepareForTestWithPythonBuildComponents, 346 d.mockFiles.AddToFixture(), 347 ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(d.errors)). 348 RunTest(t) 349 350 if len(result.Errs) > 0 { 351 return 352 } 353 354 for _, e := range d.expectedBinaries { 355 t.Run(e.name, func(t *testing.T) { 356 expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles) 357 }) 358 } 359 }) 360 } 361} 362 363func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles []string) { 364 module := ctx.ModuleForTests(name, variant) 365 366 base, baseOk := module.Module().(*PythonLibraryModule) 367 if !baseOk { 368 t.Fatalf("%s is not Python module!", name) 369 } 370 371 actualPyRunfiles := []string{} 372 for _, path := range base.srcsPathMappings { 373 actualPyRunfiles = append(actualPyRunfiles, path.dest) 374 } 375 376 android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles) 377 378 android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip) 379} 380 381func TestMain(m *testing.M) { 382 os.Exit(m.Run()) 383} 384