123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- package android
- import (
- "encoding/json"
- "os"
- "path/filepath"
- "reflect"
- "strings"
- "testing"
- "android/soong/bazel"
- "android/soong/bazel/cquery"
- analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
- "github.com/google/blueprint/metrics"
- "google.golang.org/protobuf/proto"
- )
- var testConfig = TestConfig("out", nil, "", nil)
- type testInvokeBazelContext struct{}
- type mockBazelRunner struct {
- testHelper *testing.T
- // Stores mock behavior. If an issueBazelCommand request is made for command
- // k, and {k:v} is present in this map, then the mock will return v.
- bazelCommandResults map[bazelCommand]string
- // Requests actually made of the mockBazelRunner with issueBazelCommand,
- // keyed by the command they represent.
- bazelCommandRequests map[bazelCommand]bazel.CmdRequest
- }
- func (r *mockBazelRunner) bazelCommandForRequest(cmdRequest bazel.CmdRequest) bazelCommand {
- for _, arg := range cmdRequest.Argv {
- for _, cmdType := range allBazelCommands {
- if arg == cmdType.command {
- return cmdType
- }
- }
- }
- r.testHelper.Fatalf("Unrecognized bazel request: %s", cmdRequest)
- return cqueryCmd
- }
- func (r *mockBazelRunner) issueBazelCommand(cmdRequest bazel.CmdRequest, paths *bazelPaths, eventHandler *metrics.EventHandler) (string, string, error) {
- command := r.bazelCommandForRequest(cmdRequest)
- r.bazelCommandRequests[command] = cmdRequest
- return r.bazelCommandResults[command], "", nil
- }
- func (t *testInvokeBazelContext) GetEventHandler() *metrics.EventHandler {
- return &metrics.EventHandler{}
- }
- func TestRequestResultsAfterInvokeBazel(t *testing.T) {
- label_foo := "@//foo:foo"
- label_bar := "@//foo:bar"
- apexKey := ApexConfigKey{
- WithinApex: true,
- ApexSdkVersion: "29",
- ApiDomain: "myapex",
- }
- cfg_foo := configKey{"arm64_armv8-a", Android, apexKey}
- cfg_bar := configKey{arch: "arm64_armv8-a", osType: Android}
- cmd_results := []string{
- `@//foo:foo|arm64_armv8-a|android|within_apex|29|myapex>>out/foo/foo.txt`,
- `@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
- }
- bazelContext, _ := testBazelContext(t, map[bazelCommand]string{cqueryCmd: strings.Join(cmd_results, "\n")})
- bazelContext.QueueBazelRequest(label_foo, cquery.GetOutputFiles, cfg_foo)
- bazelContext.QueueBazelRequest(label_bar, cquery.GetOutputFiles, cfg_bar)
- err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
- if err != nil {
- t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
- }
- verifyCqueryResult(t, bazelContext, label_foo, cfg_foo, "out/foo/foo.txt")
- verifyCqueryResult(t, bazelContext, label_bar, cfg_bar, "out/foo/bar.txt")
- }
- func verifyCqueryResult(t *testing.T, ctx *mixedBuildBazelContext, label string, cfg configKey, result string) {
- g, err := ctx.GetOutputFiles(label, cfg)
- if err != nil {
- t.Errorf("Expected cquery results after running InvokeBazel(), but got err %v", err)
- } else if w := []string{result}; !reflect.DeepEqual(w, g) {
- t.Errorf("Expected output %s, got %s", w, g)
- }
- }
- func TestInvokeBazelWritesBazelFiles(t *testing.T) {
- bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
- err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
- if err != nil {
- t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
- }
- if _, err := os.Stat(filepath.Join(baseDir, "soong_injection", "mixed_builds", "main.bzl")); os.IsNotExist(err) {
- t.Errorf("Expected main.bzl to exist, but it does not")
- } else if err != nil {
- t.Errorf("Unexpected error stating main.bzl %s", err)
- }
- if _, err := os.Stat(filepath.Join(baseDir, "soong_injection", "mixed_builds", "BUILD.bazel")); os.IsNotExist(err) {
- t.Errorf("Expected BUILD.bazel to exist, but it does not")
- } else if err != nil {
- t.Errorf("Unexpected error stating BUILD.bazel %s", err)
- }
- if _, err := os.Stat(filepath.Join(baseDir, "soong_injection", "WORKSPACE.bazel")); os.IsNotExist(err) {
- t.Errorf("Expected WORKSPACE.bazel to exist, but it does not")
- } else if err != nil {
- t.Errorf("Unexpected error stating WORKSPACE.bazel %s", err)
- }
- }
- func TestInvokeBazelPopulatesBuildStatements(t *testing.T) {
- type testCase struct {
- input string
- command string
- }
- var testCases = []testCase{
- {`
- {
- "artifacts": [
- { "id": 1, "path_fragment_id": 1 },
- { "id": 2, "path_fragment_id": 2 }],
- "actions": [{
- "target_Id": 1,
- "action_Key": "x",
- "mnemonic": "x",
- "arguments": ["touch", "foo"],
- "input_dep_set_ids": [1],
- "output_Ids": [1],
- "primary_output_id": 1
- }],
- "dep_set_of_files": [
- { "id": 1, "direct_artifact_ids": [1, 2] }],
- "path_fragments": [
- { "id": 1, "label": "one" },
- { "id": 2, "label": "two" }]
- }`,
- "cd 'test/exec_root' && rm -rf 'one' && touch foo",
- }, {`
- {
- "artifacts": [
- { "id": 1, "path_fragment_id": 10 },
- { "id": 2, "path_fragment_id": 20 }],
- "actions": [{
- "target_Id": 100,
- "action_Key": "x",
- "mnemonic": "x",
- "arguments": ["bogus", "command"],
- "output_Ids": [1, 2],
- "primary_output_id": 1
- }],
- "path_fragments": [
- { "id": 10, "label": "one", "parent_id": 30 },
- { "id": 20, "label": "one.d", "parent_id": 30 },
- { "id": 30, "label": "parent" }]
- }`,
- `cd 'test/exec_root' && rm -rf 'parent/one' && bogus command && sed -i'' -E 's@(^|\s|")bazel-out/@\1test/bazel_out/@g' 'parent/one.d'`,
- },
- }
- for i, testCase := range testCases {
- data, err := JsonToActionGraphContainer(testCase.input)
- if err != nil {
- t.Error(err)
- }
- bazelContext, _ := testBazelContext(t, map[bazelCommand]string{aqueryCmd: string(data)})
- err = bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
- if err != nil {
- t.Fatalf("testCase #%d: did not expect error invoking Bazel, but got %s", i+1, err)
- }
- got := bazelContext.BuildStatementsToRegister()
- if want := 1; len(got) != want {
- t.Fatalf("expected %d registered build statements, but got %#v", want, got)
- }
- cmd := RuleBuilderCommand{}
- ctx := builderContextForTests{PathContextForTesting(TestConfig("out", nil, "", nil))}
- createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", ctx)
- if actual, expected := cmd.buf.String(), testCase.command; expected != actual {
- t.Errorf("expected: [%s], actual: [%s]", expected, actual)
- }
- }
- }
- func TestCoverageFlagsAfterInvokeBazel(t *testing.T) {
- testConfig.productVariables.ClangCoverage = boolPtr(true)
- testConfig.productVariables.NativeCoveragePaths = []string{"foo1", "foo2"}
- testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1", "bar2"}
- verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+foo1,+foo2,-bar1,-bar2")
- testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
- testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
- verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+foo1,-bar1")
- testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
- testConfig.productVariables.NativeCoverageExcludePaths = nil
- verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+foo1")
- testConfig.productVariables.NativeCoveragePaths = nil
- testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
- verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=-bar1")
- testConfig.productVariables.NativeCoveragePaths = []string{"*"}
- testConfig.productVariables.NativeCoverageExcludePaths = nil
- verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+.*")
- testConfig.productVariables.ClangCoverage = boolPtr(false)
- verifyAqueryDoesNotContainSubstrings(t, testConfig, "collect_code_coverage", "instrumentation_filter")
- }
- func TestBazelRequestsSorted(t *testing.T) {
- bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
- cfgKeyArm64Android := configKey{arch: "arm64_armv8-a", osType: Android}
- cfgKeyArm64Linux := configKey{arch: "arm64_armv8-a", osType: Linux}
- cfgKeyOtherAndroid := configKey{arch: "otherarch", osType: Android}
- bazelContext.QueueBazelRequest("zzz", cquery.GetOutputFiles, cfgKeyArm64Android)
- bazelContext.QueueBazelRequest("ccc", cquery.GetApexInfo, cfgKeyArm64Android)
- bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, cfgKeyArm64Android)
- bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, cfgKeyArm64Android)
- bazelContext.QueueBazelRequest("xxx", cquery.GetOutputFiles, cfgKeyArm64Linux)
- bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, cfgKeyArm64Android)
- bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, cfgKeyOtherAndroid)
- bazelContext.QueueBazelRequest("bbb", cquery.GetOutputFiles, cfgKeyOtherAndroid)
- if len(bazelContext.requests) != 7 {
- t.Error("Expected 7 request elements, but got", len(bazelContext.requests))
- }
- lastString := ""
- for _, val := range bazelContext.requests {
- thisString := val.String()
- if thisString <= lastString {
- t.Errorf("Requests are not ordered correctly. '%s' came before '%s'", lastString, thisString)
- }
- lastString = thisString
- }
- }
- func TestIsModuleNameAllowed(t *testing.T) {
- libDisabled := "lib_disabled"
- libEnabled := "lib_enabled"
- libDclaWithinApex := "lib_dcla_within_apex"
- libDclaNonApex := "lib_dcla_non_apex"
- libNotConverted := "lib_not_converted"
- disabledModules := map[string]bool{
- libDisabled: true,
- }
- enabledModules := map[string]bool{
- libEnabled: true,
- }
- dclaEnabledModules := map[string]bool{
- libDclaWithinApex: true,
- libDclaNonApex: true,
- }
- bazelContext := &mixedBuildBazelContext{
- bazelEnabledModules: enabledModules,
- bazelDisabledModules: disabledModules,
- bazelDclaEnabledModules: dclaEnabledModules,
- }
- if bazelContext.IsModuleNameAllowed(libDisabled, true) {
- t.Fatalf("%s shouldn't be allowed for mixed build", libDisabled)
- }
- if !bazelContext.IsModuleNameAllowed(libEnabled, true) {
- t.Fatalf("%s should be allowed for mixed build", libEnabled)
- }
- if !bazelContext.IsModuleNameAllowed(libDclaWithinApex, true) {
- t.Fatalf("%s should be allowed for mixed build", libDclaWithinApex)
- }
- if bazelContext.IsModuleNameAllowed(libDclaNonApex, false) {
- t.Fatalf("%s shouldn't be allowed for mixed build", libDclaNonApex)
- }
- if bazelContext.IsModuleNameAllowed(libNotConverted, true) {
- t.Fatalf("%s shouldn't be allowed for mixed build", libNotConverted)
- }
- }
- func verifyAqueryContainsFlags(t *testing.T, config Config, expected ...string) {
- t.Helper()
- bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
- err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{})
- if err != nil {
- t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
- }
- sliceContains := func(slice []string, x string) bool {
- for _, s := range slice {
- if s == x {
- return true
- }
- }
- return false
- }
- aqueryArgv := bazelContext.bazelRunner.(*mockBazelRunner).bazelCommandRequests[aqueryCmd].Argv
- for _, expectedFlag := range expected {
- if !sliceContains(aqueryArgv, expectedFlag) {
- t.Errorf("aquery does not contain expected flag %#v. Argv was: %#v", expectedFlag, aqueryArgv)
- }
- }
- }
- func verifyAqueryDoesNotContainSubstrings(t *testing.T, config Config, substrings ...string) {
- t.Helper()
- bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
- err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{})
- if err != nil {
- t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
- }
- sliceContainsSubstring := func(slice []string, substring string) bool {
- for _, s := range slice {
- if strings.Contains(s, substring) {
- return true
- }
- }
- return false
- }
- aqueryArgv := bazelContext.bazelRunner.(*mockBazelRunner).bazelCommandRequests[aqueryCmd].Argv
- for _, substring := range substrings {
- if sliceContainsSubstring(aqueryArgv, substring) {
- t.Errorf("aquery contains unexpected substring %#v. Argv was: %#v", substring, aqueryArgv)
- }
- }
- }
- func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*mixedBuildBazelContext, string) {
- t.Helper()
- p := bazelPaths{
- soongOutDir: t.TempDir(),
- outputBase: "outputbase",
- workspaceDir: "workspace_dir",
- }
- if _, exists := bazelCommandResults[aqueryCmd]; !exists {
- bazelCommandResults[aqueryCmd] = ""
- }
- runner := &mockBazelRunner{
- testHelper: t,
- bazelCommandResults: bazelCommandResults,
- bazelCommandRequests: map[bazelCommand]bazel.CmdRequest{},
- }
- return &mixedBuildBazelContext{
- bazelRunner: runner,
- paths: &p,
- }, p.soongOutDir
- }
- // Transform the json format to ActionGraphContainer
- func JsonToActionGraphContainer(inputString string) ([]byte, error) {
- var aqueryProtoResult analysis_v2_proto.ActionGraphContainer
- err := json.Unmarshal([]byte(inputString), &aqueryProtoResult)
- if err != nil {
- return []byte(""), err
- }
- data, _ := proto.Marshal(&aqueryProtoResult)
- return data, err
- }
|