rust_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  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. package rust
  15. import (
  16. "io/ioutil"
  17. "os"
  18. "runtime"
  19. "strings"
  20. "testing"
  21. "github.com/google/blueprint/proptools"
  22. "android/soong/android"
  23. "android/soong/cc"
  24. )
  25. var (
  26. buildDir string
  27. )
  28. func setUp() {
  29. var err error
  30. buildDir, err = ioutil.TempDir("", "soong_rust_test")
  31. if err != nil {
  32. panic(err)
  33. }
  34. }
  35. func tearDown() {
  36. os.RemoveAll(buildDir)
  37. }
  38. func TestMain(m *testing.M) {
  39. run := func() int {
  40. setUp()
  41. defer tearDown()
  42. return m.Run()
  43. }
  44. os.Exit(run())
  45. }
  46. // testRust returns a TestContext in which a basic environment has been setup.
  47. // This environment contains a few mocked files. See testRustCtx.useMockedFs
  48. // for the list of these files.
  49. func testRust(t *testing.T, bp string) *android.TestContext {
  50. tctx := newTestRustCtx(t, bp)
  51. tctx.useMockedFs()
  52. tctx.generateConfig()
  53. return tctx.parse(t)
  54. }
  55. // testRustCov returns a TestContext in which a basic environment has been
  56. // setup. This environment explicitly enables coverage.
  57. func testRustCov(t *testing.T, bp string) *android.TestContext {
  58. tctx := newTestRustCtx(t, bp)
  59. tctx.useMockedFs()
  60. tctx.generateConfig()
  61. tctx.enableCoverage(t)
  62. return tctx.parse(t)
  63. }
  64. // testRustError ensures that at least one error was raised and its value
  65. // matches the pattern provided. The error can be either in the parsing of the
  66. // Blueprint or when generating the build actions.
  67. func testRustError(t *testing.T, pattern string, bp string) {
  68. tctx := newTestRustCtx(t, bp)
  69. tctx.useMockedFs()
  70. tctx.generateConfig()
  71. tctx.parseError(t, pattern)
  72. }
  73. // testRustCtx is used to build a particular test environment. Unless your
  74. // tests requires a specific setup, prefer the wrapping functions: testRust,
  75. // testRustCov or testRustError.
  76. type testRustCtx struct {
  77. bp string
  78. fs map[string][]byte
  79. env map[string]string
  80. config *android.Config
  81. }
  82. // newTestRustCtx returns a new testRustCtx for the Blueprint definition argument.
  83. func newTestRustCtx(t *testing.T, bp string) *testRustCtx {
  84. // TODO (b/140435149)
  85. if runtime.GOOS != "linux" {
  86. t.Skip("Rust Soong tests can only be run on Linux hosts currently")
  87. }
  88. return &testRustCtx{bp: bp}
  89. }
  90. // useMockedFs setup a default mocked filesystem for the test environment.
  91. func (tctx *testRustCtx) useMockedFs() {
  92. tctx.fs = map[string][]byte{
  93. "foo.rs": nil,
  94. "foo.c": nil,
  95. "src/bar.rs": nil,
  96. "src/any.h": nil,
  97. "buf.proto": nil,
  98. "liby.so": nil,
  99. "libz.so": nil,
  100. }
  101. }
  102. // generateConfig creates the android.Config based on the bp, fs and env
  103. // attributes of the testRustCtx.
  104. func (tctx *testRustCtx) generateConfig() {
  105. tctx.bp = tctx.bp + GatherRequiredDepsForTest()
  106. cc.GatherRequiredFilesForTest(tctx.fs)
  107. config := android.TestArchConfig(buildDir, tctx.env, tctx.bp, tctx.fs)
  108. tctx.config = &config
  109. }
  110. // enableCoverage configures the test to enable coverage.
  111. func (tctx *testRustCtx) enableCoverage(t *testing.T) {
  112. if tctx.config == nil {
  113. t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
  114. }
  115. tctx.config.TestProductVariables.GcovCoverage = proptools.BoolPtr(true)
  116. tctx.config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
  117. tctx.config.TestProductVariables.NativeCoveragePaths = []string{"*"}
  118. }
  119. // parse validates the configuration and parses the Blueprint file. It returns
  120. // a TestContext which can be used to retrieve the generated modules via
  121. // ModuleForTests.
  122. func (tctx testRustCtx) parse(t *testing.T) *android.TestContext {
  123. if tctx.config == nil {
  124. t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
  125. }
  126. ctx := CreateTestContext()
  127. ctx.Register(*tctx.config)
  128. _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
  129. android.FailIfErrored(t, errs)
  130. _, errs = ctx.PrepareBuildActions(*tctx.config)
  131. android.FailIfErrored(t, errs)
  132. return ctx
  133. }
  134. // parseError parses the Blueprint file and ensure that at least one error
  135. // matching the provided pattern is observed.
  136. func (tctx testRustCtx) parseError(t *testing.T, pattern string) {
  137. if tctx.config == nil {
  138. t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
  139. }
  140. ctx := CreateTestContext()
  141. ctx.Register(*tctx.config)
  142. _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
  143. if len(errs) > 0 {
  144. android.FailIfNoMatchingErrors(t, pattern, errs)
  145. return
  146. }
  147. _, errs = ctx.PrepareBuildActions(*tctx.config)
  148. if len(errs) > 0 {
  149. android.FailIfNoMatchingErrors(t, pattern, errs)
  150. return
  151. }
  152. t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
  153. }
  154. // Test that we can extract the link path from a lib path.
  155. func TestLinkPathFromFilePath(t *testing.T) {
  156. barPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so")
  157. libName := linkPathFromFilePath(barPath)
  158. expectedResult := "out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/"
  159. if libName != expectedResult {
  160. t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libName)
  161. }
  162. }
  163. // Test to make sure dependencies are being picked up correctly.
  164. func TestDepsTracking(t *testing.T) {
  165. ctx := testRust(t, `
  166. rust_ffi_host_static {
  167. name: "libstatic",
  168. srcs: ["foo.rs"],
  169. crate_name: "static",
  170. }
  171. rust_ffi_host_shared {
  172. name: "libshared",
  173. srcs: ["foo.rs"],
  174. crate_name: "shared",
  175. }
  176. rust_library_host_dylib {
  177. name: "libdylib",
  178. srcs: ["foo.rs"],
  179. crate_name: "dylib",
  180. }
  181. rust_library_host_rlib {
  182. name: "librlib",
  183. srcs: ["foo.rs"],
  184. crate_name: "rlib",
  185. }
  186. rust_proc_macro {
  187. name: "libpm",
  188. srcs: ["foo.rs"],
  189. crate_name: "pm",
  190. }
  191. rust_binary_host {
  192. name: "fizz-buzz",
  193. dylibs: ["libdylib"],
  194. rlibs: ["librlib"],
  195. proc_macros: ["libpm"],
  196. static_libs: ["libstatic"],
  197. shared_libs: ["libshared"],
  198. srcs: ["foo.rs"],
  199. }
  200. `)
  201. module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
  202. // Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
  203. if !android.InList("libdylib", module.Properties.AndroidMkDylibs) {
  204. t.Errorf("Dylib dependency not detected (dependency missing from AndroidMkDylibs)")
  205. }
  206. if !android.InList("librlib.rlib-std", module.Properties.AndroidMkRlibs) {
  207. t.Errorf("Rlib dependency not detected (dependency missing from AndroidMkRlibs)")
  208. }
  209. if !android.InList("libpm", module.Properties.AndroidMkProcMacroLibs) {
  210. t.Errorf("Proc_macro dependency not detected (dependency missing from AndroidMkProcMacroLibs)")
  211. }
  212. if !android.InList("libshared", module.Properties.AndroidMkSharedLibs) {
  213. t.Errorf("Shared library dependency not detected (dependency missing from AndroidMkSharedLibs)")
  214. }
  215. if !android.InList("libstatic", module.Properties.AndroidMkStaticLibs) {
  216. t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)")
  217. }
  218. }
  219. func TestSourceProviderDeps(t *testing.T) {
  220. ctx := testRust(t, `
  221. rust_binary {
  222. name: "fizz-buzz-dep",
  223. srcs: [
  224. "foo.rs",
  225. ":my_generator",
  226. ":libbindings",
  227. ],
  228. rlibs: ["libbindings"],
  229. }
  230. rust_proc_macro {
  231. name: "libprocmacro",
  232. srcs: [
  233. "foo.rs",
  234. ":my_generator",
  235. ":libbindings",
  236. ],
  237. rlibs: ["libbindings"],
  238. crate_name: "procmacro",
  239. }
  240. rust_library {
  241. name: "libfoo",
  242. srcs: [
  243. "foo.rs",
  244. ":my_generator",
  245. ":libbindings",
  246. ],
  247. rlibs: ["libbindings"],
  248. crate_name: "foo",
  249. }
  250. genrule {
  251. name: "my_generator",
  252. tools: ["any_rust_binary"],
  253. cmd: "$(location) -o $(out) $(in)",
  254. srcs: ["src/any.h"],
  255. out: ["src/any.rs"],
  256. }
  257. rust_bindgen {
  258. name: "libbindings",
  259. crate_name: "bindings",
  260. source_stem: "bindings",
  261. host_supported: true,
  262. wrapper_src: "src/any.h",
  263. }
  264. `)
  265. libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Rule("rustc")
  266. if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/bindings.rs") {
  267. t.Errorf("rust_bindgen generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings())
  268. }
  269. if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/any.rs") {
  270. t.Errorf("genrule generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings())
  271. }
  272. fizzBuzz := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Rule("rustc")
  273. if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/bindings.rs") {
  274. t.Errorf("rust_bindgen generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings())
  275. }
  276. if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/any.rs") {
  277. t.Errorf("genrule generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings())
  278. }
  279. libprocmacro := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Rule("rustc")
  280. if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/bindings.rs") {
  281. t.Errorf("rust_bindgen generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings())
  282. }
  283. if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/any.rs") {
  284. t.Errorf("genrule generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings())
  285. }
  286. // Check that our bindings are picked up as crate dependencies as well
  287. libfooMod := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
  288. if !android.InList("libbindings.dylib-std", libfooMod.Properties.AndroidMkRlibs) {
  289. t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
  290. }
  291. fizzBuzzMod := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Module().(*Module)
  292. if !android.InList("libbindings.dylib-std", fizzBuzzMod.Properties.AndroidMkRlibs) {
  293. t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
  294. }
  295. libprocmacroMod := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Module().(*Module)
  296. if !android.InList("libbindings.rlib-std", libprocmacroMod.Properties.AndroidMkRlibs) {
  297. t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
  298. }
  299. }
  300. func TestSourceProviderTargetMismatch(t *testing.T) {
  301. // This might error while building the dependency tree or when calling depsToPaths() depending on the lunched
  302. // target, which results in two different errors. So don't check the error, just confirm there is one.
  303. testRustError(t, ".*", `
  304. rust_proc_macro {
  305. name: "libprocmacro",
  306. srcs: [
  307. "foo.rs",
  308. ":libbindings",
  309. ],
  310. crate_name: "procmacro",
  311. }
  312. rust_bindgen {
  313. name: "libbindings",
  314. crate_name: "bindings",
  315. source_stem: "bindings",
  316. wrapper_src: "src/any.h",
  317. }
  318. `)
  319. }
  320. // Test to make sure proc_macros use host variants when building device modules.
  321. func TestProcMacroDeviceDeps(t *testing.T) {
  322. ctx := testRust(t, `
  323. rust_library_host_rlib {
  324. name: "libbar",
  325. srcs: ["foo.rs"],
  326. crate_name: "bar",
  327. }
  328. rust_proc_macro {
  329. name: "libpm",
  330. rlibs: ["libbar"],
  331. srcs: ["foo.rs"],
  332. crate_name: "pm",
  333. }
  334. rust_binary {
  335. name: "fizz-buzz",
  336. proc_macros: ["libpm"],
  337. srcs: ["foo.rs"],
  338. }
  339. `)
  340. rustc := ctx.ModuleForTests("libpm", "linux_glibc_x86_64").Rule("rustc")
  341. if !strings.Contains(rustc.Args["libFlags"], "libbar/linux_glibc_x86_64") {
  342. t.Errorf("Proc_macro is not using host variant of dependent modules.")
  343. }
  344. }
  345. // Test that no_stdlibs suppresses dependencies on rust standard libraries
  346. func TestNoStdlibs(t *testing.T) {
  347. ctx := testRust(t, `
  348. rust_binary {
  349. name: "fizz-buzz",
  350. srcs: ["foo.rs"],
  351. no_stdlibs: true,
  352. }`)
  353. module := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Module().(*Module)
  354. if android.InList("libstd", module.Properties.AndroidMkDylibs) {
  355. t.Errorf("no_stdlibs did not suppress dependency on libstd")
  356. }
  357. }
  358. // Test that libraries provide both 32-bit and 64-bit variants.
  359. func TestMultilib(t *testing.T) {
  360. ctx := testRust(t, `
  361. rust_library_rlib {
  362. name: "libfoo",
  363. srcs: ["foo.rs"],
  364. crate_name: "foo",
  365. }`)
  366. _ = ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std")
  367. _ = ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_rlib_dylib-std")
  368. }