bazel_handler_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. package android
  2. import (
  3. "encoding/json"
  4. "os"
  5. "path/filepath"
  6. "reflect"
  7. "strings"
  8. "testing"
  9. "android/soong/bazel"
  10. "android/soong/bazel/cquery"
  11. analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
  12. "github.com/google/blueprint/metrics"
  13. "google.golang.org/protobuf/proto"
  14. )
  15. var testConfig = TestConfig("out", nil, "", nil)
  16. type testInvokeBazelContext struct{}
  17. type mockBazelRunner struct {
  18. testHelper *testing.T
  19. // Stores mock behavior. If an issueBazelCommand request is made for command
  20. // k, and {k:v} is present in this map, then the mock will return v.
  21. bazelCommandResults map[bazelCommand]string
  22. // Requests actually made of the mockBazelRunner with issueBazelCommand,
  23. // keyed by the command they represent.
  24. bazelCommandRequests map[bazelCommand]bazel.CmdRequest
  25. }
  26. func (r *mockBazelRunner) bazelCommandForRequest(cmdRequest bazel.CmdRequest) bazelCommand {
  27. for _, arg := range cmdRequest.Argv {
  28. for _, cmdType := range allBazelCommands {
  29. if arg == cmdType.command {
  30. return cmdType
  31. }
  32. }
  33. }
  34. r.testHelper.Fatalf("Unrecognized bazel request: %s", cmdRequest)
  35. return cqueryCmd
  36. }
  37. func (r *mockBazelRunner) issueBazelCommand(cmdRequest bazel.CmdRequest, paths *bazelPaths, eventHandler *metrics.EventHandler) (string, string, error) {
  38. command := r.bazelCommandForRequest(cmdRequest)
  39. r.bazelCommandRequests[command] = cmdRequest
  40. return r.bazelCommandResults[command], "", nil
  41. }
  42. func (t *testInvokeBazelContext) GetEventHandler() *metrics.EventHandler {
  43. return &metrics.EventHandler{}
  44. }
  45. func TestRequestResultsAfterInvokeBazel(t *testing.T) {
  46. label_foo := "@//foo:foo"
  47. label_bar := "@//foo:bar"
  48. apexKey := ApexConfigKey{
  49. WithinApex: true,
  50. ApexSdkVersion: "29",
  51. ApiDomain: "myapex",
  52. }
  53. cfg_foo := configKey{"arm64_armv8-a", Android, apexKey}
  54. cfg_bar := configKey{arch: "arm64_armv8-a", osType: Android}
  55. cmd_results := []string{
  56. `@//foo:foo|arm64_armv8-a|android|within_apex|29|myapex>>out/foo/foo.txt`,
  57. `@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
  58. }
  59. bazelContext, _ := testBazelContext(t, map[bazelCommand]string{cqueryCmd: strings.Join(cmd_results, "\n")})
  60. bazelContext.QueueBazelRequest(label_foo, cquery.GetOutputFiles, cfg_foo)
  61. bazelContext.QueueBazelRequest(label_bar, cquery.GetOutputFiles, cfg_bar)
  62. err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
  63. if err != nil {
  64. t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
  65. }
  66. verifyCqueryResult(t, bazelContext, label_foo, cfg_foo, "out/foo/foo.txt")
  67. verifyCqueryResult(t, bazelContext, label_bar, cfg_bar, "out/foo/bar.txt")
  68. }
  69. func verifyCqueryResult(t *testing.T, ctx *mixedBuildBazelContext, label string, cfg configKey, result string) {
  70. g, err := ctx.GetOutputFiles(label, cfg)
  71. if err != nil {
  72. t.Errorf("Expected cquery results after running InvokeBazel(), but got err %v", err)
  73. } else if w := []string{result}; !reflect.DeepEqual(w, g) {
  74. t.Errorf("Expected output %s, got %s", w, g)
  75. }
  76. }
  77. func TestInvokeBazelWritesBazelFiles(t *testing.T) {
  78. bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
  79. err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
  80. if err != nil {
  81. t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
  82. }
  83. if _, err := os.Stat(filepath.Join(baseDir, "soong_injection", "mixed_builds", "main.bzl")); os.IsNotExist(err) {
  84. t.Errorf("Expected main.bzl to exist, but it does not")
  85. } else if err != nil {
  86. t.Errorf("Unexpected error stating main.bzl %s", err)
  87. }
  88. if _, err := os.Stat(filepath.Join(baseDir, "soong_injection", "mixed_builds", "BUILD.bazel")); os.IsNotExist(err) {
  89. t.Errorf("Expected BUILD.bazel to exist, but it does not")
  90. } else if err != nil {
  91. t.Errorf("Unexpected error stating BUILD.bazel %s", err)
  92. }
  93. if _, err := os.Stat(filepath.Join(baseDir, "soong_injection", "WORKSPACE.bazel")); os.IsNotExist(err) {
  94. t.Errorf("Expected WORKSPACE.bazel to exist, but it does not")
  95. } else if err != nil {
  96. t.Errorf("Unexpected error stating WORKSPACE.bazel %s", err)
  97. }
  98. }
  99. func TestInvokeBazelPopulatesBuildStatements(t *testing.T) {
  100. type testCase struct {
  101. input string
  102. command string
  103. }
  104. var testCases = []testCase{
  105. {`
  106. {
  107. "artifacts": [
  108. { "id": 1, "path_fragment_id": 1 },
  109. { "id": 2, "path_fragment_id": 2 }],
  110. "actions": [{
  111. "target_Id": 1,
  112. "action_Key": "x",
  113. "mnemonic": "x",
  114. "arguments": ["touch", "foo"],
  115. "input_dep_set_ids": [1],
  116. "output_Ids": [1],
  117. "primary_output_id": 1
  118. }],
  119. "dep_set_of_files": [
  120. { "id": 1, "direct_artifact_ids": [1, 2] }],
  121. "path_fragments": [
  122. { "id": 1, "label": "one" },
  123. { "id": 2, "label": "two" }]
  124. }`,
  125. "cd 'test/exec_root' && rm -rf 'one' && touch foo",
  126. }, {`
  127. {
  128. "artifacts": [
  129. { "id": 1, "path_fragment_id": 10 },
  130. { "id": 2, "path_fragment_id": 20 }],
  131. "actions": [{
  132. "target_Id": 100,
  133. "action_Key": "x",
  134. "mnemonic": "x",
  135. "arguments": ["bogus", "command"],
  136. "output_Ids": [1, 2],
  137. "primary_output_id": 1
  138. }],
  139. "path_fragments": [
  140. { "id": 10, "label": "one", "parent_id": 30 },
  141. { "id": 20, "label": "one.d", "parent_id": 30 },
  142. { "id": 30, "label": "parent" }]
  143. }`,
  144. `cd 'test/exec_root' && rm -rf 'parent/one' && bogus command && sed -i'' -E 's@(^|\s|")bazel-out/@\1test/bazel_out/@g' 'parent/one.d'`,
  145. },
  146. }
  147. for i, testCase := range testCases {
  148. data, err := JsonToActionGraphContainer(testCase.input)
  149. if err != nil {
  150. t.Error(err)
  151. }
  152. bazelContext, _ := testBazelContext(t, map[bazelCommand]string{aqueryCmd: string(data)})
  153. err = bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
  154. if err != nil {
  155. t.Fatalf("testCase #%d: did not expect error invoking Bazel, but got %s", i+1, err)
  156. }
  157. got := bazelContext.BuildStatementsToRegister()
  158. if want := 1; len(got) != want {
  159. t.Fatalf("expected %d registered build statements, but got %#v", want, got)
  160. }
  161. cmd := RuleBuilderCommand{}
  162. ctx := builderContextForTests{PathContextForTesting(TestConfig("out", nil, "", nil))}
  163. createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", ctx)
  164. if actual, expected := cmd.buf.String(), testCase.command; expected != actual {
  165. t.Errorf("expected: [%s], actual: [%s]", expected, actual)
  166. }
  167. }
  168. }
  169. func TestCoverageFlagsAfterInvokeBazel(t *testing.T) {
  170. testConfig.productVariables.ClangCoverage = boolPtr(true)
  171. testConfig.productVariables.NativeCoveragePaths = []string{"foo1", "foo2"}
  172. testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1", "bar2"}
  173. verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+foo1,+foo2,-bar1,-bar2")
  174. testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
  175. testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
  176. verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+foo1,-bar1")
  177. testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
  178. testConfig.productVariables.NativeCoverageExcludePaths = nil
  179. verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+foo1")
  180. testConfig.productVariables.NativeCoveragePaths = nil
  181. testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
  182. verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=-bar1")
  183. testConfig.productVariables.NativeCoveragePaths = []string{"*"}
  184. testConfig.productVariables.NativeCoverageExcludePaths = nil
  185. verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+.*")
  186. testConfig.productVariables.ClangCoverage = boolPtr(false)
  187. verifyAqueryDoesNotContainSubstrings(t, testConfig, "collect_code_coverage", "instrumentation_filter")
  188. }
  189. func TestBazelRequestsSorted(t *testing.T) {
  190. bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
  191. cfgKeyArm64Android := configKey{arch: "arm64_armv8-a", osType: Android}
  192. cfgKeyArm64Linux := configKey{arch: "arm64_armv8-a", osType: Linux}
  193. cfgKeyOtherAndroid := configKey{arch: "otherarch", osType: Android}
  194. bazelContext.QueueBazelRequest("zzz", cquery.GetOutputFiles, cfgKeyArm64Android)
  195. bazelContext.QueueBazelRequest("ccc", cquery.GetApexInfo, cfgKeyArm64Android)
  196. bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, cfgKeyArm64Android)
  197. bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, cfgKeyArm64Android)
  198. bazelContext.QueueBazelRequest("xxx", cquery.GetOutputFiles, cfgKeyArm64Linux)
  199. bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, cfgKeyArm64Android)
  200. bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, cfgKeyOtherAndroid)
  201. bazelContext.QueueBazelRequest("bbb", cquery.GetOutputFiles, cfgKeyOtherAndroid)
  202. if len(bazelContext.requests) != 7 {
  203. t.Error("Expected 7 request elements, but got", len(bazelContext.requests))
  204. }
  205. lastString := ""
  206. for _, val := range bazelContext.requests {
  207. thisString := val.String()
  208. if thisString <= lastString {
  209. t.Errorf("Requests are not ordered correctly. '%s' came before '%s'", lastString, thisString)
  210. }
  211. lastString = thisString
  212. }
  213. }
  214. func TestIsModuleNameAllowed(t *testing.T) {
  215. libDisabled := "lib_disabled"
  216. libEnabled := "lib_enabled"
  217. libDclaWithinApex := "lib_dcla_within_apex"
  218. libDclaNonApex := "lib_dcla_non_apex"
  219. libNotConverted := "lib_not_converted"
  220. disabledModules := map[string]bool{
  221. libDisabled: true,
  222. }
  223. enabledModules := map[string]bool{
  224. libEnabled: true,
  225. }
  226. dclaEnabledModules := map[string]bool{
  227. libDclaWithinApex: true,
  228. libDclaNonApex: true,
  229. }
  230. bazelContext := &mixedBuildBazelContext{
  231. bazelEnabledModules: enabledModules,
  232. bazelDisabledModules: disabledModules,
  233. bazelDclaEnabledModules: dclaEnabledModules,
  234. }
  235. if bazelContext.IsModuleNameAllowed(libDisabled, true) {
  236. t.Fatalf("%s shouldn't be allowed for mixed build", libDisabled)
  237. }
  238. if !bazelContext.IsModuleNameAllowed(libEnabled, true) {
  239. t.Fatalf("%s should be allowed for mixed build", libEnabled)
  240. }
  241. if !bazelContext.IsModuleNameAllowed(libDclaWithinApex, true) {
  242. t.Fatalf("%s should be allowed for mixed build", libDclaWithinApex)
  243. }
  244. if bazelContext.IsModuleNameAllowed(libDclaNonApex, false) {
  245. t.Fatalf("%s shouldn't be allowed for mixed build", libDclaNonApex)
  246. }
  247. if bazelContext.IsModuleNameAllowed(libNotConverted, true) {
  248. t.Fatalf("%s shouldn't be allowed for mixed build", libNotConverted)
  249. }
  250. }
  251. func verifyAqueryContainsFlags(t *testing.T, config Config, expected ...string) {
  252. t.Helper()
  253. bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
  254. err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{})
  255. if err != nil {
  256. t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
  257. }
  258. sliceContains := func(slice []string, x string) bool {
  259. for _, s := range slice {
  260. if s == x {
  261. return true
  262. }
  263. }
  264. return false
  265. }
  266. aqueryArgv := bazelContext.bazelRunner.(*mockBazelRunner).bazelCommandRequests[aqueryCmd].Argv
  267. for _, expectedFlag := range expected {
  268. if !sliceContains(aqueryArgv, expectedFlag) {
  269. t.Errorf("aquery does not contain expected flag %#v. Argv was: %#v", expectedFlag, aqueryArgv)
  270. }
  271. }
  272. }
  273. func verifyAqueryDoesNotContainSubstrings(t *testing.T, config Config, substrings ...string) {
  274. t.Helper()
  275. bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
  276. err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{})
  277. if err != nil {
  278. t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
  279. }
  280. sliceContainsSubstring := func(slice []string, substring string) bool {
  281. for _, s := range slice {
  282. if strings.Contains(s, substring) {
  283. return true
  284. }
  285. }
  286. return false
  287. }
  288. aqueryArgv := bazelContext.bazelRunner.(*mockBazelRunner).bazelCommandRequests[aqueryCmd].Argv
  289. for _, substring := range substrings {
  290. if sliceContainsSubstring(aqueryArgv, substring) {
  291. t.Errorf("aquery contains unexpected substring %#v. Argv was: %#v", substring, aqueryArgv)
  292. }
  293. }
  294. }
  295. func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*mixedBuildBazelContext, string) {
  296. t.Helper()
  297. p := bazelPaths{
  298. soongOutDir: t.TempDir(),
  299. outputBase: "outputbase",
  300. workspaceDir: "workspace_dir",
  301. }
  302. if _, exists := bazelCommandResults[aqueryCmd]; !exists {
  303. bazelCommandResults[aqueryCmd] = ""
  304. }
  305. runner := &mockBazelRunner{
  306. testHelper: t,
  307. bazelCommandResults: bazelCommandResults,
  308. bazelCommandRequests: map[bazelCommand]bazel.CmdRequest{},
  309. }
  310. return &mixedBuildBazelContext{
  311. bazelRunner: runner,
  312. paths: &p,
  313. }, p.soongOutDir
  314. }
  315. // Transform the json format to ActionGraphContainer
  316. func JsonToActionGraphContainer(inputString string) ([]byte, error) {
  317. var aqueryProtoResult analysis_v2_proto.ActionGraphContainer
  318. err := json.Unmarshal([]byte(inputString), &aqueryProtoResult)
  319. if err != nil {
  320. return []byte(""), err
  321. }
  322. data, _ := proto.Marshal(&aqueryProtoResult)
  323. return data, err
  324. }