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 "regexp" 22 "testing" 23 24 "android/soong/android" 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 moduleVariantErrTemplate = "%s: module %q variant %q: " 38 pkgPathErrTemplate = moduleVariantErrTemplate + 39 "pkg_path: %q must be a relative path contained in par file." 40 badIdentifierErrTemplate = moduleVariantErrTemplate + 41 "srcs: the path %q contains invalid subpath %q." 42 dupRunfileErrTemplate = moduleVariantErrTemplate + 43 "found two files to be placed at the same location within zip %q." + 44 " First file: in module %s at path %q." + 45 " Second file: in module %s at path %q." 46 noSrcFileErr = moduleVariantErrTemplate + "doesn't have any source files!" 47 badSrcFileExtErr = moduleVariantErrTemplate + "srcs: found non (.py|.proto) file: %q!" 48 badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py|.proto) file: %q!" 49 bpFile = "Android.bp" 50 51 data = []struct { 52 desc string 53 mockFiles android.MockFS 54 55 errors []string 56 expectedBinaries []pyModule 57 }{ 58 { 59 desc: "module without any src files", 60 mockFiles: map[string][]byte{ 61 filepath.Join("dir", bpFile): []byte( 62 `python_library_host { 63 name: "lib1", 64 }`, 65 ), 66 }, 67 errors: []string{ 68 fmt.Sprintf(noSrcFileErr, 69 "dir/Android.bp:1:1", "lib1", "PY3"), 70 }, 71 }, 72 { 73 desc: "module with bad src file ext", 74 mockFiles: map[string][]byte{ 75 filepath.Join("dir", bpFile): []byte( 76 `python_library_host { 77 name: "lib1", 78 srcs: [ 79 "file1.exe", 80 ], 81 }`, 82 ), 83 "dir/file1.exe": nil, 84 }, 85 errors: []string{ 86 fmt.Sprintf(badSrcFileExtErr, 87 "dir/Android.bp:3:11", "lib1", "PY3", "dir/file1.exe"), 88 }, 89 }, 90 { 91 desc: "module with bad data file ext", 92 mockFiles: map[string][]byte{ 93 filepath.Join("dir", bpFile): []byte( 94 `python_library_host { 95 name: "lib1", 96 srcs: [ 97 "file1.py", 98 ], 99 data: [ 100 "file2.py", 101 ], 102 }`, 103 ), 104 "dir/file1.py": nil, 105 "dir/file2.py": nil, 106 }, 107 errors: []string{ 108 fmt.Sprintf(badDataFileExtErr, 109 "dir/Android.bp:6:11", "lib1", "PY3", "dir/file2.py"), 110 }, 111 }, 112 { 113 desc: "module with bad pkg_path format", 114 mockFiles: map[string][]byte{ 115 filepath.Join("dir", bpFile): []byte( 116 `python_library_host { 117 name: "lib1", 118 pkg_path: "a/c/../../", 119 srcs: [ 120 "file1.py", 121 ], 122 } 123 124 python_library_host { 125 name: "lib2", 126 pkg_path: "a/c/../../../", 127 srcs: [ 128 "file1.py", 129 ], 130 } 131 132 python_library_host { 133 name: "lib3", 134 pkg_path: "/a/c/../../", 135 srcs: [ 136 "file1.py", 137 ], 138 }`, 139 ), 140 "dir/file1.py": nil, 141 }, 142 errors: []string{ 143 fmt.Sprintf(pkgPathErrTemplate, 144 "dir/Android.bp:11:15", "lib2", "PY3", "a/c/../../../"), 145 fmt.Sprintf(pkgPathErrTemplate, 146 "dir/Android.bp:19:15", "lib3", "PY3", "/a/c/../../"), 147 }, 148 }, 149 { 150 desc: "module with bad runfile src path format", 151 mockFiles: map[string][]byte{ 152 filepath.Join("dir", bpFile): []byte( 153 `python_library_host { 154 name: "lib1", 155 pkg_path: "a/b/c/", 156 srcs: [ 157 ".file1.py", 158 "123/file1.py", 159 "-e/f/file1.py", 160 ], 161 }`, 162 ), 163 "dir/.file1.py": nil, 164 "dir/123/file1.py": nil, 165 "dir/-e/f/file1.py": nil, 166 }, 167 errors: []string{ 168 fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11", 169 "lib1", "PY3", "a/b/c/-e/f/file1.py", "-e"), 170 fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11", 171 "lib1", "PY3", "a/b/c/.file1.py", ".file1"), 172 fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11", 173 "lib1", "PY3", "a/b/c/123/file1.py", "123"), 174 }, 175 }, 176 { 177 desc: "module with duplicate runfile path", 178 mockFiles: map[string][]byte{ 179 filepath.Join("dir", bpFile): []byte( 180 `python_library_host { 181 name: "lib1", 182 pkg_path: "a/b/", 183 srcs: [ 184 "c/file1.py", 185 ], 186 } 187 188 python_library_host { 189 name: "lib2", 190 pkg_path: "a/b/c/", 191 srcs: [ 192 "file1.py", 193 ], 194 libs: [ 195 "lib1", 196 ], 197 } 198 199 python_binary_host { 200 name: "bin", 201 pkg_path: "e/", 202 srcs: [ 203 "bin.py", 204 ], 205 libs: [ 206 "lib2", 207 ], 208 } 209 `, 210 ), 211 "dir/c/file1.py": nil, 212 "dir/file1.py": nil, 213 "dir/bin.py": nil, 214 }, 215 errors: []string{ 216 fmt.Sprintf(dupRunfileErrTemplate, "dir/Android.bp:20:6", 217 "bin", "PY3", "a/b/c/file1.py", "bin", "dir/file1.py", 218 "lib1", "dir/c/file1.py"), 219 }, 220 }, 221 { 222 desc: "module for testing dependencies", 223 mockFiles: map[string][]byte{ 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 "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 depsSrcsZips: []string{ 318 "out/soong/.intermediates/dir/lib5/PY3/lib5.py.srcszip", 319 "out/soong/.intermediates/dir/lib6/PY3/lib6.py.srcszip", 320 }, 321 }, 322 }, 323 }, 324 } 325) 326 327func TestPythonModule(t *testing.T) { 328 for _, d := range data { 329 if d.desc != "module with duplicate runfile path" { 330 continue 331 } 332 errorPatterns := make([]string, len(d.errors)) 333 for i, s := range d.errors { 334 errorPatterns[i] = regexp.QuoteMeta(s) 335 } 336 337 t.Run(d.desc, func(t *testing.T) { 338 result := android.GroupFixturePreparers( 339 android.PrepareForTestWithDefaults, 340 PrepareForTestWithPythonBuildComponents, 341 d.mockFiles.AddToFixture(), 342 ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(errorPatterns)). 343 RunTest(t) 344 345 if len(result.Errs) > 0 { 346 return 347 } 348 349 for _, e := range d.expectedBinaries { 350 t.Run(e.name, func(t *testing.T) { 351 expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles, e.depsSrcsZips) 352 }) 353 } 354 }) 355 } 356} 357 358func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles, expectedDepsSrcsZips []string) { 359 module := ctx.ModuleForTests(name, variant) 360 361 base, baseOk := module.Module().(*Module) 362 if !baseOk { 363 t.Fatalf("%s is not Python module!", name) 364 } 365 366 actualPyRunfiles := []string{} 367 for _, path := range base.srcsPathMappings { 368 actualPyRunfiles = append(actualPyRunfiles, path.dest) 369 } 370 371 android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles) 372 373 android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip) 374 375 android.AssertPathsRelativeToTopEquals(t, "depsSrcsZips", expectedDepsSrcsZips, base.depsSrcsZips) 376} 377 378func TestMain(m *testing.M) { 379 os.Exit(m.Run()) 380} 381