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