• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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