1// Copyright 2019 The Android Open Source Project 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 rust 16 17import ( 18 "os" 19 "runtime" 20 "strings" 21 "testing" 22 23 "github.com/google/blueprint/proptools" 24 25 "android/soong/android" 26 "android/soong/genrule" 27) 28 29func TestMain(m *testing.M) { 30 os.Exit(m.Run()) 31} 32 33var prepareForRustTest = android.GroupFixturePreparers( 34 android.PrepareForTestWithArchMutator, 35 android.PrepareForTestWithDefaults, 36 android.PrepareForTestWithPrebuilts, 37 38 genrule.PrepareForTestWithGenRuleBuildComponents, 39 40 PrepareForTestWithRustIncludeVndk, 41 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { 42 variables.DeviceVndkVersion = StringPtr("current") 43 variables.ProductVndkVersion = StringPtr("current") 44 variables.Platform_vndk_version = StringPtr("29") 45 }), 46) 47 48var rustMockedFiles = android.MockFS{ 49 "foo.rs": nil, 50 "foo.c": nil, 51 "src/bar.rs": nil, 52 "src/any.h": nil, 53 "c_includes/c_header.h": nil, 54 "rust_includes/rust_headers.h": nil, 55 "proto.proto": nil, 56 "proto/buf.proto": nil, 57 "buf.proto": nil, 58 "foo.proto": nil, 59 "liby.so": nil, 60 "libz.so": nil, 61 "data.txt": nil, 62 "liblog.map.txt": nil, 63} 64 65// testRust returns a TestContext in which a basic environment has been setup. 66// This environment contains a few mocked files. See rustMockedFiles for the list of these files. 67func testRust(t *testing.T, bp string) *android.TestContext { 68 skipTestIfOsNotSupported(t) 69 result := android.GroupFixturePreparers( 70 prepareForRustTest, 71 rustMockedFiles.AddToFixture(), 72 ). 73 RunTestWithBp(t, bp) 74 return result.TestContext 75} 76 77func testRustVndk(t *testing.T, bp string) *android.TestContext { 78 return testRustVndkFs(t, bp, rustMockedFiles) 79} 80 81const ( 82 sharedVendorVariant = "android_vendor.29_arm64_armv8-a_shared" 83 rlibVendorVariant = "android_vendor.29_arm64_armv8-a_rlib_rlib-std" 84 sharedRecoveryVariant = "android_recovery_arm64_armv8-a_shared" 85 rlibRecoveryVariant = "android_recovery_arm64_armv8-a_rlib_rlib-std" 86 binaryCoreVariant = "android_arm64_armv8-a" 87 binaryVendorVariant = "android_vendor.29_arm64_armv8-a" 88 binaryProductVariant = "android_product.29_arm64_armv8-a" 89 binaryRecoveryVariant = "android_recovery_arm64_armv8-a" 90) 91 92func testRustVndkFs(t *testing.T, bp string, fs android.MockFS) *android.TestContext { 93 return testRustVndkFsVersions(t, bp, fs, "current", "current", "29") 94} 95 96func testRustVndkFsVersions(t *testing.T, bp string, fs android.MockFS, device_version, product_version, vndk_version string) *android.TestContext { 97 skipTestIfOsNotSupported(t) 98 result := android.GroupFixturePreparers( 99 prepareForRustTest, 100 fs.AddToFixture(), 101 android.FixtureModifyProductVariables( 102 func(variables android.FixtureProductVariables) { 103 variables.DeviceVndkVersion = StringPtr(device_version) 104 variables.ProductVndkVersion = StringPtr(product_version) 105 variables.Platform_vndk_version = StringPtr(vndk_version) 106 }, 107 ), 108 ).RunTestWithBp(t, bp) 109 return result.TestContext 110} 111 112func testRustRecoveryFsVersions(t *testing.T, bp string, fs android.MockFS, device_version, vndk_version, recovery_version string) *android.TestContext { 113 skipTestIfOsNotSupported(t) 114 result := android.GroupFixturePreparers( 115 prepareForRustTest, 116 fs.AddToFixture(), 117 android.FixtureModifyProductVariables( 118 func(variables android.FixtureProductVariables) { 119 variables.DeviceVndkVersion = StringPtr(device_version) 120 variables.RecoverySnapshotVersion = StringPtr(recovery_version) 121 variables.Platform_vndk_version = StringPtr(vndk_version) 122 }, 123 ), 124 ).RunTestWithBp(t, bp) 125 return result.TestContext 126} 127 128// testRustCov returns a TestContext in which a basic environment has been 129// setup. This environment explicitly enables coverage. 130func testRustCov(t *testing.T, bp string) *android.TestContext { 131 skipTestIfOsNotSupported(t) 132 result := android.GroupFixturePreparers( 133 prepareForRustTest, 134 rustMockedFiles.AddToFixture(), 135 android.FixtureModifyProductVariables( 136 func(variables android.FixtureProductVariables) { 137 variables.ClangCoverage = proptools.BoolPtr(true) 138 variables.Native_coverage = proptools.BoolPtr(true) 139 variables.NativeCoveragePaths = []string{"*"} 140 }, 141 ), 142 ).RunTestWithBp(t, bp) 143 return result.TestContext 144} 145 146// testRustError ensures that at least one error was raised and its value 147// matches the pattern provided. The error can be either in the parsing of the 148// Blueprint or when generating the build actions. 149func testRustError(t *testing.T, pattern string, bp string) { 150 skipTestIfOsNotSupported(t) 151 android.GroupFixturePreparers( 152 prepareForRustTest, 153 rustMockedFiles.AddToFixture(), 154 ). 155 ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)). 156 RunTestWithBp(t, bp) 157} 158 159// testRustVndkError is similar to testRustError, but can be used to test VNDK-related errors. 160func testRustVndkError(t *testing.T, pattern string, bp string) { 161 testRustVndkFsError(t, pattern, bp, rustMockedFiles) 162} 163 164func testRustVndkFsError(t *testing.T, pattern string, bp string, fs android.MockFS) { 165 skipTestIfOsNotSupported(t) 166 android.GroupFixturePreparers( 167 prepareForRustTest, 168 fs.AddToFixture(), 169 android.FixtureModifyProductVariables( 170 func(variables android.FixtureProductVariables) { 171 variables.DeviceVndkVersion = StringPtr("current") 172 variables.ProductVndkVersion = StringPtr("current") 173 variables.Platform_vndk_version = StringPtr("VER") 174 }, 175 ), 176 ). 177 ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)). 178 RunTestWithBp(t, bp) 179} 180 181// testRustCtx is used to build a particular test environment. Unless your 182// tests requires a specific setup, prefer the wrapping functions: testRust, 183// testRustCov or testRustError. 184type testRustCtx struct { 185 bp string 186 fs map[string][]byte 187 env map[string]string 188 config *android.Config 189} 190 191func skipTestIfOsNotSupported(t *testing.T) { 192 // TODO (b/140435149) 193 if runtime.GOOS != "linux" { 194 t.Skip("Rust Soong tests can only be run on Linux hosts currently") 195 } 196} 197 198// Test that we can extract the link path from a lib path. 199func TestLinkPathFromFilePath(t *testing.T) { 200 barPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so") 201 libName := linkPathFromFilePath(barPath) 202 expectedResult := "out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/" 203 204 if libName != expectedResult { 205 t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libName) 206 } 207} 208 209// Test to make sure dependencies are being picked up correctly. 210func TestDepsTracking(t *testing.T) { 211 ctx := testRust(t, ` 212 cc_library { 213 host_supported: true, 214 name: "cc_stubs_dep", 215 } 216 rust_ffi_host_static { 217 name: "libstatic", 218 srcs: ["foo.rs"], 219 crate_name: "static", 220 } 221 rust_ffi_host_static { 222 name: "libwholestatic", 223 srcs: ["foo.rs"], 224 crate_name: "wholestatic", 225 } 226 rust_ffi_host_shared { 227 name: "libshared", 228 srcs: ["foo.rs"], 229 crate_name: "shared", 230 } 231 rust_library_host_dylib { 232 name: "libdylib", 233 srcs: ["foo.rs"], 234 crate_name: "dylib", 235 } 236 rust_library_host_rlib { 237 name: "librlib", 238 srcs: ["foo.rs"], 239 crate_name: "rlib", 240 static_libs: ["libstatic"], 241 whole_static_libs: ["libwholestatic"], 242 shared_libs: ["cc_stubs_dep"], 243 } 244 rust_proc_macro { 245 name: "libpm", 246 srcs: ["foo.rs"], 247 crate_name: "pm", 248 } 249 rust_binary_host { 250 name: "fizz-buzz", 251 dylibs: ["libdylib"], 252 rlibs: ["librlib"], 253 proc_macros: ["libpm"], 254 static_libs: ["libstatic"], 255 shared_libs: ["libshared"], 256 srcs: ["foo.rs"], 257 } 258 `) 259 module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module) 260 rustc := ctx.ModuleForTests("librlib", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc") 261 rustLink := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Rule("rustLink") 262 263 // Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up. 264 if !android.InList("libdylib", module.Properties.AndroidMkDylibs) { 265 t.Errorf("Dylib dependency not detected (dependency missing from AndroidMkDylibs)") 266 } 267 268 if !android.InList("librlib.rlib-std", module.Properties.AndroidMkRlibs) { 269 t.Errorf("Rlib dependency not detected (dependency missing from AndroidMkRlibs)") 270 } 271 272 if !android.InList("libpm", module.Properties.AndroidMkProcMacroLibs) { 273 t.Errorf("Proc_macro dependency not detected (dependency missing from AndroidMkProcMacroLibs)") 274 } 275 276 if !android.InList("libshared", module.Properties.AndroidMkSharedLibs) { 277 t.Errorf("Shared library dependency not detected (dependency missing from AndroidMkSharedLibs)") 278 } 279 280 if !android.InList("libstatic", module.Properties.AndroidMkStaticLibs) { 281 t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)") 282 } 283 284 if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic=wholestatic") { 285 t.Errorf("-lstatic flag not being passed to rustc for static library %#v", rustc.Args["rustcFlags"]) 286 } 287 288 if !strings.Contains(rustLink.Args["linkFlags"], "cc_stubs_dep.so") { 289 t.Errorf("shared cc_library not being passed to rustc linkFlags %#v", rustLink.Args["linkFlags"]) 290 } 291 292 if !android.SuffixInList(rustLink.OrderOnly.Strings(), "cc_stubs_dep.so") { 293 t.Errorf("shared cc dep not being passed as order-only to rustc %#v", rustLink.OrderOnly.Strings()) 294 } 295 296 if !android.SuffixInList(rustLink.Implicits.Strings(), "cc_stubs_dep.so.toc") { 297 t.Errorf("shared cc dep TOC not being passed as implicit to rustc %#v", rustLink.Implicits.Strings()) 298 } 299} 300 301func TestSourceProviderDeps(t *testing.T) { 302 ctx := testRust(t, ` 303 rust_binary { 304 name: "fizz-buzz-dep", 305 srcs: [ 306 "foo.rs", 307 ":my_generator", 308 ":libbindings", 309 ], 310 rlibs: ["libbindings"], 311 } 312 rust_proc_macro { 313 name: "libprocmacro", 314 srcs: [ 315 "foo.rs", 316 ":my_generator", 317 ":libbindings", 318 ], 319 rlibs: ["libbindings"], 320 crate_name: "procmacro", 321 } 322 rust_library { 323 name: "libfoo", 324 srcs: [ 325 "foo.rs", 326 ":my_generator", 327 ":libbindings", 328 ], 329 rlibs: ["libbindings"], 330 crate_name: "foo", 331 } 332 genrule { 333 name: "my_generator", 334 tools: ["any_rust_binary"], 335 cmd: "$(location) -o $(out) $(in)", 336 srcs: ["src/any.h"], 337 out: ["src/any.rs"], 338 } 339 rust_binary_host { 340 name: "any_rust_binary", 341 srcs: [ 342 "foo.rs", 343 ], 344 } 345 rust_bindgen { 346 name: "libbindings", 347 crate_name: "bindings", 348 source_stem: "bindings", 349 host_supported: true, 350 wrapper_src: "src/any.h", 351 } 352 `) 353 354 libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Rule("rustc") 355 if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/bindings.rs") { 356 t.Errorf("rust_bindgen generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings()) 357 } 358 if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/any.rs") { 359 t.Errorf("genrule generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings()) 360 } 361 362 fizzBuzz := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Rule("rustc") 363 if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/bindings.rs") { 364 t.Errorf("rust_bindgen generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings()) 365 } 366 if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/any.rs") { 367 t.Errorf("genrule generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings()) 368 } 369 370 libprocmacro := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Rule("rustc") 371 if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/bindings.rs") { 372 t.Errorf("rust_bindgen generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings()) 373 } 374 if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/any.rs") { 375 t.Errorf("genrule generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings()) 376 } 377 378 // Check that our bindings are picked up as crate dependencies as well 379 libfooMod := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module) 380 if !android.InList("libbindings.dylib-std", libfooMod.Properties.AndroidMkRlibs) { 381 t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)") 382 } 383 fizzBuzzMod := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Module().(*Module) 384 if !android.InList("libbindings.dylib-std", fizzBuzzMod.Properties.AndroidMkRlibs) { 385 t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)") 386 } 387 libprocmacroMod := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Module().(*Module) 388 if !android.InList("libbindings.rlib-std", libprocmacroMod.Properties.AndroidMkRlibs) { 389 t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)") 390 } 391} 392 393func TestSourceProviderTargetMismatch(t *testing.T) { 394 // This might error while building the dependency tree or when calling depsToPaths() depending on the lunched 395 // target, which results in two different errors. So don't check the error, just confirm there is one. 396 testRustError(t, ".*", ` 397 rust_proc_macro { 398 name: "libprocmacro", 399 srcs: [ 400 "foo.rs", 401 ":libbindings", 402 ], 403 crate_name: "procmacro", 404 } 405 rust_bindgen { 406 name: "libbindings", 407 crate_name: "bindings", 408 source_stem: "bindings", 409 wrapper_src: "src/any.h", 410 } 411 `) 412} 413 414// Test to make sure proc_macros use host variants when building device modules. 415func TestProcMacroDeviceDeps(t *testing.T) { 416 ctx := testRust(t, ` 417 rust_library_host_rlib { 418 name: "libbar", 419 srcs: ["foo.rs"], 420 crate_name: "bar", 421 } 422 rust_proc_macro { 423 name: "libpm", 424 rlibs: ["libbar"], 425 srcs: ["foo.rs"], 426 crate_name: "pm", 427 } 428 rust_binary { 429 name: "fizz-buzz", 430 proc_macros: ["libpm"], 431 srcs: ["foo.rs"], 432 } 433 `) 434 rustc := ctx.ModuleForTests("libpm", "linux_glibc_x86_64").Rule("rustc") 435 436 if !strings.Contains(rustc.Args["libFlags"], "libbar/linux_glibc_x86_64") { 437 t.Errorf("Proc_macro is not using host variant of dependent modules.") 438 } 439} 440 441// Test that no_stdlibs suppresses dependencies on rust standard libraries 442func TestNoStdlibs(t *testing.T) { 443 ctx := testRust(t, ` 444 rust_binary { 445 name: "fizz-buzz", 446 srcs: ["foo.rs"], 447 no_stdlibs: true, 448 }`) 449 module := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Module().(*Module) 450 451 if android.InList("libstd", module.Properties.AndroidMkDylibs) { 452 t.Errorf("no_stdlibs did not suppress dependency on libstd") 453 } 454} 455 456// Test that libraries provide both 32-bit and 64-bit variants. 457func TestMultilib(t *testing.T) { 458 ctx := testRust(t, ` 459 rust_library_rlib { 460 name: "libfoo", 461 srcs: ["foo.rs"], 462 crate_name: "foo", 463 }`) 464 465 _ = ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std") 466 _ = ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_rlib_dylib-std") 467} 468 469// Test that library size measurements are generated. 470func TestLibrarySizes(t *testing.T) { 471 ctx := testRust(t, ` 472 rust_library_dylib { 473 name: "libwaldo", 474 srcs: ["foo.rs"], 475 crate_name: "waldo", 476 }`) 477 478 m := ctx.SingletonForTests("file_metrics") 479 m.Output("unstripped/libwaldo.dylib.so.bloaty.csv") 480 m.Output("libwaldo.dylib.so.bloaty.csv") 481} 482 483func assertString(t *testing.T, got, expected string) { 484 t.Helper() 485 if got != expected { 486 t.Errorf("expected %q got %q", expected, got) 487 } 488} 489