| // Copyright 2017 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // This file implements the logic of bpfix and also provides a programmatic interface |
| |
| package bpfix |
| |
| import ( |
| "bytes" |
| "fmt" |
| "reflect" |
| "strings" |
| "testing" |
| |
| "github.com/google/blueprint/parser" |
| "github.com/google/blueprint/pathtools" |
| ) |
| |
| // TODO(jeffrygaston) remove this when position is removed from ParseNode (in b/38325146) and we can directly do reflect.DeepEqual |
| func printListOfStrings(items []string) (text string) { |
| if len(items) == 0 { |
| return "[]" |
| } |
| return fmt.Sprintf("[\"%s\"]", strings.Join(items, "\", \"")) |
| |
| } |
| |
| func buildTree(local_include_dirs []string, export_include_dirs []string) (file *parser.File, errs []error) { |
| // TODO(jeffrygaston) use the builder class when b/38325146 is done |
| input := fmt.Sprintf(`cc_library_shared { |
| name: "iAmAModule", |
| local_include_dirs: %s, |
| export_include_dirs: %s, |
| } |
| `, |
| printListOfStrings(local_include_dirs), printListOfStrings(export_include_dirs)) |
| tree, errs := parser.Parse("", strings.NewReader(input), parser.NewScope(nil)) |
| if len(errs) > 0 { |
| errs = append([]error{fmt.Errorf("failed to parse:\n%s", input)}, errs...) |
| } |
| return tree, errs |
| } |
| |
| func implFilterListTest(t *testing.T, local_include_dirs []string, export_include_dirs []string, expectedResult []string) { |
| // build tree |
| tree, errs := buildTree(local_include_dirs, export_include_dirs) |
| if len(errs) > 0 { |
| t.Error("failed to build tree") |
| for _, err := range errs { |
| t.Error(err) |
| } |
| t.Fatalf("%d parse errors", len(errs)) |
| } |
| |
| fixer := NewFixer(tree) |
| |
| // apply simplifications |
| err := runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther)(fixer) |
| if len(errs) > 0 { |
| t.Fatal(err) |
| } |
| |
| // lookup legacy property |
| mod := fixer.tree.Defs[0].(*parser.Module) |
| |
| expectedResultString := fmt.Sprintf("%q", expectedResult) |
| if expectedResult == nil { |
| expectedResultString = "unset" |
| } |
| |
| // check that the value for the legacy property was updated to the correct value |
| errorHeader := fmt.Sprintf("\nFailed to correctly simplify key 'local_include_dirs' in the presence of 'export_include_dirs.'\n"+ |
| "original local_include_dirs: %q\n"+ |
| "original export_include_dirs: %q\n"+ |
| "expected result: %s\n"+ |
| "actual result: ", |
| local_include_dirs, export_include_dirs, expectedResultString) |
| result, found := mod.GetProperty("local_include_dirs") |
| if !found { |
| if expectedResult == nil { |
| return |
| } |
| t.Fatal(errorHeader + "property not found") |
| } |
| |
| listResult, ok := result.Value.(*parser.List) |
| if !ok { |
| t.Fatalf("%sproperty is not a list: %v", errorHeader, listResult) |
| } |
| |
| if expectedResult == nil { |
| t.Fatalf("%sproperty exists: %v", errorHeader, listResult) |
| } |
| |
| actualExpressions := listResult.Values |
| actualValues := make([]string, 0) |
| for _, expr := range actualExpressions { |
| str := expr.(*parser.String) |
| actualValues = append(actualValues, str.Value) |
| } |
| |
| if !reflect.DeepEqual(actualValues, expectedResult) { |
| t.Fatalf("%s%q\nlists are different", errorHeader, actualValues) |
| } |
| } |
| |
| func TestSimplifyKnownVariablesDuplicatingEachOther(t *testing.T) { |
| // TODO use []Expression{} once buildTree above can support it (which is after b/38325146 is done) |
| implFilterListTest(t, []string{"include"}, []string{"include"}, nil) |
| implFilterListTest(t, []string{"include1"}, []string{"include2"}, []string{"include1"}) |
| implFilterListTest(t, []string{"include1", "include2", "include3", "include4"}, []string{"include2"}, |
| []string{"include1", "include3", "include4"}) |
| implFilterListTest(t, []string{}, []string{"include"}, []string{}) |
| implFilterListTest(t, []string{}, []string{}, []string{}) |
| } |
| |
| func checkError(t *testing.T, in, expectedErr string, innerTest func(*Fixer) error) { |
| expected := preProcessOutErr(expectedErr) |
| runTestOnce(t, in, expected, innerTest) |
| } |
| |
| func runTestOnce(t *testing.T, in, expected string, innerTest func(*Fixer) error) { |
| fixer, err := preProcessIn(in) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| out, err := runFixerOnce(fixer, innerTest) |
| if err != nil { |
| out = err.Error() |
| } |
| |
| compareResult := compareOutExpected(in, out, expected) |
| if len(compareResult) > 0 { |
| t.Errorf(compareResult) |
| } |
| } |
| |
| func preProcessOutErr(expectedErr string) string { |
| expected := strings.TrimSpace(expectedErr) |
| return expected |
| } |
| |
| func preProcessOut(out string) (expected string, err error) { |
| expected, err = Reformat(out) |
| if err != nil { |
| return expected, err |
| } |
| return expected, nil |
| } |
| |
| func preProcessIn(in string) (fixer *Fixer, err error) { |
| in, err = Reformat(in) |
| if err != nil { |
| return fixer, err |
| } |
| |
| tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil)) |
| if errs != nil { |
| return fixer, err |
| } |
| |
| fixer = NewFixer(tree) |
| |
| return fixer, nil |
| } |
| |
| func runFixerOnce(fixer *Fixer, innerTest func(*Fixer) error) (string, error) { |
| err := innerTest(fixer) |
| if err != nil { |
| return "", err |
| } |
| |
| out, err := parser.Print(fixer.tree) |
| if err != nil { |
| return "", err |
| } |
| return string(out), nil |
| } |
| |
| func compareOutExpected(in, out, expected string) string { |
| if out != expected { |
| return fmt.Sprintf("output didn't match:\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n", |
| in, expected, out) |
| } |
| return "" |
| } |
| |
| func runPassOnce(t *testing.T, in, out string, innerTest func(*Fixer) error) { |
| expected, err := preProcessOut(out) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| runTestOnce(t, in, expected, innerTest) |
| } |
| |
| func runPass(t *testing.T, in, out string, innerTest func(*Fixer) error) { |
| expected, err := preProcessOut(out) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| fixer, err := preProcessIn(in) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| got := "" |
| prev := "foo" |
| passes := 0 |
| for got != prev && passes < 10 { |
| out, err = runFixerOnce(fixer, innerTest) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| prev = got |
| got = string(out) |
| passes++ |
| } |
| |
| compareResult := compareOutExpected(in, out, expected) |
| if len(compareResult) > 0 { |
| t.Errorf(compareResult) |
| } |
| } |
| |
| func TestMergeMatchingProperties(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "empty", |
| in: ` |
| java_library { |
| name: "foo", |
| static_libs: [], |
| static_libs: [], |
| } |
| `, |
| out: ` |
| java_library { |
| name: "foo", |
| static_libs: [], |
| } |
| `, |
| }, |
| { |
| name: "single line into multiline", |
| in: ` |
| java_library { |
| name: "foo", |
| static_libs: [ |
| "a", |
| "b", |
| ], |
| //c1 |
| static_libs: ["c" /*c2*/], |
| } |
| `, |
| out: ` |
| java_library { |
| name: "foo", |
| static_libs: [ |
| "a", |
| "b", |
| "c", /*c2*/ |
| ], |
| //c1 |
| } |
| `, |
| }, |
| { |
| name: "multiline into multiline", |
| in: ` |
| java_library { |
| name: "foo", |
| static_libs: [ |
| "a", |
| "b", |
| ], |
| //c1 |
| static_libs: [ |
| //c2 |
| "c", //c3 |
| "d", |
| ], |
| } |
| `, |
| out: ` |
| java_library { |
| name: "foo", |
| static_libs: [ |
| "a", |
| "b", |
| //c2 |
| "c", //c3 |
| "d", |
| ], |
| //c1 |
| } |
| `, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, func(fixer *Fixer) error { |
| return runPatchListMod(mergeMatchingModuleProperties)(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestReorderCommonProperties(t *testing.T) { |
| var tests = []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "empty", |
| in: `cc_library {}`, |
| out: `cc_library {}`, |
| }, |
| { |
| name: "only priority", |
| in: ` |
| cc_library { |
| name: "foo", |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| } |
| `, |
| }, |
| { |
| name: "already in order", |
| in: ` |
| cc_library { |
| name: "foo", |
| defaults: ["bar"], |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| defaults: ["bar"], |
| } |
| `, |
| }, |
| { |
| name: "reorder only priority", |
| in: ` |
| cc_library { |
| defaults: ["bar"], |
| name: "foo", |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| defaults: ["bar"], |
| } |
| `, |
| }, |
| { |
| name: "reorder", |
| in: ` |
| cc_library { |
| name: "foo", |
| srcs: ["a.c"], |
| host_supported: true, |
| defaults: ["bar"], |
| shared_libs: ["baz"], |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| defaults: ["bar"], |
| host_supported: true, |
| srcs: ["a.c"], |
| shared_libs: ["baz"], |
| } |
| `, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, func(fixer *Fixer) error { |
| return runPatchListMod(reorderCommonProperties)(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestRemoveMatchingModuleListProperties(t *testing.T) { |
| var tests = []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "simple", |
| in: ` |
| cc_library { |
| name: "foo", |
| foo: ["a"], |
| bar: ["a"], |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| bar: ["a"], |
| } |
| `, |
| }, |
| { |
| name: "long", |
| in: ` |
| cc_library { |
| name: "foo", |
| foo: [ |
| "a", |
| "b", |
| ], |
| bar: ["a"], |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| foo: [ |
| "b", |
| ], |
| bar: ["a"], |
| } |
| `, |
| }, |
| { |
| name: "long fully removed", |
| in: ` |
| cc_library { |
| name: "foo", |
| foo: [ |
| "a", |
| ], |
| bar: ["a"], |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| bar: ["a"], |
| } |
| `, |
| }, |
| { |
| name: "comment", |
| in: ` |
| cc_library { |
| name: "foo", |
| |
| // comment |
| foo: ["a"], |
| |
| bar: ["a"], |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| |
| // comment |
| |
| bar: ["a"], |
| } |
| `, |
| }, |
| { |
| name: "inner comment", |
| in: ` |
| cc_library { |
| name: "foo", |
| foo: [ |
| // comment |
| "a", |
| ], |
| bar: ["a"], |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| bar: ["a"], |
| } |
| `, |
| }, |
| { |
| name: "eol comment", |
| in: ` |
| cc_library { |
| name: "foo", |
| foo: ["a"], // comment |
| bar: ["a"], |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| // comment |
| bar: ["a"], |
| } |
| `, |
| }, |
| { |
| name: "eol comment with blank lines", |
| in: ` |
| cc_library { |
| name: "foo", |
| |
| foo: ["a"], // comment |
| |
| // bar |
| bar: ["a"], |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| |
| // comment |
| |
| // bar |
| bar: ["a"], |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, func(fixer *Fixer) error { |
| return runPatchListMod(func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error { |
| return removeMatchingModuleListProperties(mod, patchList, "bar", "foo") |
| })(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestReplaceJavaStaticLibs(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "static lib", |
| in: ` |
| java_library_static { |
| name: "foo", |
| } |
| `, |
| out: ` |
| java_library { |
| name: "foo", |
| } |
| `, |
| }, |
| { |
| name: "java lib", |
| in: ` |
| java_library { |
| name: "foo", |
| } |
| `, |
| out: ` |
| java_library { |
| name: "foo", |
| } |
| `, |
| }, |
| { |
| name: "java installable lib", |
| in: ` |
| java_library { |
| name: "foo", |
| installable: true, |
| } |
| `, |
| out: ` |
| java_library { |
| name: "foo", |
| installable: true, |
| } |
| `, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, func(fixer *Fixer) error { |
| return rewriteJavaStaticLibs(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestRewritePrebuilts(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "jar srcs", |
| in: ` |
| java_import { |
| name: "foo", |
| srcs: ["foo.jar"], |
| } |
| `, |
| out: ` |
| java_import { |
| name: "foo", |
| jars: ["foo.jar"], |
| } |
| `, |
| }, |
| { |
| name: "aar srcs", |
| in: ` |
| java_import { |
| name: "foo", |
| srcs: ["foo.aar"], |
| installable: true, |
| } |
| `, |
| out: ` |
| android_library_import { |
| name: "foo", |
| aars: ["foo.aar"], |
| |
| } |
| `, |
| }, |
| { |
| name: "host prebuilt", |
| in: ` |
| java_import { |
| name: "foo", |
| srcs: ["foo.jar"], |
| host: true, |
| } |
| `, |
| out: ` |
| java_import_host { |
| name: "foo", |
| jars: ["foo.jar"], |
| |
| } |
| `, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, func(fixer *Fixer) error { |
| return rewriteIncorrectAndroidmkPrebuilts(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestRewriteCtsModuleTypes(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "cts_support_package", |
| in: ` |
| cts_support_package { |
| name: "foo", |
| } |
| `, |
| out: ` |
| android_test_helper_app { |
| name: "foo", |
| defaults: ["cts_support_defaults"], |
| } |
| `, |
| }, |
| { |
| name: "cts_package", |
| in: ` |
| cts_package { |
| name: "foo", |
| } |
| `, |
| out: ` |
| android_test { |
| name: "foo", |
| defaults: ["cts_defaults"], |
| } |
| `, |
| }, |
| { |
| name: "cts_target_java_library", |
| in: ` |
| cts_target_java_library { |
| name: "foo", |
| } |
| `, |
| out: ` |
| java_library { |
| name: "foo", |
| defaults: ["cts_defaults"], |
| } |
| `, |
| }, |
| { |
| name: "cts_host_java_library", |
| in: ` |
| cts_host_java_library { |
| name: "foo", |
| } |
| `, |
| out: ` |
| java_library_host { |
| name: "foo", |
| defaults: ["cts_defaults"], |
| } |
| `, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, rewriteCtsModuleTypes) |
| }) |
| } |
| } |
| |
| func TestRewritePrebuiltEtc(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "prebuilt_etc src", |
| in: ` |
| prebuilt_etc { |
| name: "foo", |
| srcs: ["bar"], |
| } |
| `, |
| out: `prebuilt_etc { |
| name: "foo", |
| src: "bar", |
| } |
| `, |
| }, |
| { |
| name: "prebuilt_etc src", |
| in: ` |
| prebuilt_etc { |
| name: "foo", |
| srcs: FOO, |
| } |
| `, |
| out: `prebuilt_etc { |
| name: "foo", |
| src: FOO, |
| } |
| `, |
| }, |
| { |
| name: "prebuilt_etc src", |
| in: ` |
| prebuilt_etc { |
| name: "foo", |
| srcs: ["bar", "baz"], |
| } |
| `, |
| out: `prebuilt_etc { |
| name: "foo", |
| src: "ERROR: LOCAL_SRC_FILES should contain at most one item", |
| |
| } |
| `, |
| }, |
| { |
| name: "prebuilt_etc sub_dir", |
| in: ` |
| prebuilt_etc { |
| name: "foo", |
| src: "bar", |
| sub_dir: "baz", |
| } |
| `, |
| out: `prebuilt_etc { |
| name: "foo", |
| src: "bar", |
| relative_install_path: "baz", |
| } |
| `, |
| }, |
| { |
| name: "prebuilt_etc sub_dir", |
| in: ` |
| prebuilt_etc_host { |
| name: "foo", |
| src: "bar", |
| local_module_path: { |
| var: "HOST_OUT", |
| fixed: "/etc/baz", |
| }, |
| } |
| `, |
| out: `prebuilt_etc_host { |
| name: "foo", |
| src: "bar", |
| relative_install_path: "baz", |
| |
| } |
| `, |
| }, |
| { |
| name: "prebuilt_etc sub_dir", |
| in: ` |
| prebuilt_etc_host { |
| name: "foo", |
| src: "bar", |
| local_module_path: { |
| var: "HOST_OUT", |
| fixed: "/baz/sub", |
| }, |
| } |
| `, |
| out: `prebuilt_root_host { |
| name: "foo", |
| src: "bar", |
| relative_install_path: "baz/sub", |
| |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, func(fixer *Fixer) error { |
| return rewriteAndroidmkPrebuiltEtc(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestRewriteAndroidTest(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "android_test valid module path", |
| in: ` |
| android_test { |
| name: "foo", |
| local_module_path: { |
| var: "TARGET_OUT_DATA_APPS", |
| }, |
| } |
| `, |
| out: ` |
| android_test { |
| name: "foo", |
| |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, func(fixer *Fixer) error { |
| return rewriteAndroidTest(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestRewriteAndroidAppImport(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "android_app_import apk", |
| in: ` |
| android_app_import { |
| name: "foo", |
| srcs: ["package.apk"], |
| } |
| `, |
| out: ` |
| android_app_import { |
| name: "foo", |
| apk: "package.apk", |
| } |
| `, |
| }, |
| { |
| name: "android_app_import presigned", |
| in: ` |
| android_app_import { |
| name: "foo", |
| apk: "package.apk", |
| certificate: "PRESIGNED", |
| } |
| `, |
| out: ` |
| android_app_import { |
| name: "foo", |
| apk: "package.apk", |
| presigned: true, |
| |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, func(fixer *Fixer) error { |
| return rewriteAndroidAppImport(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestRemoveEmptyLibDependencies(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "remove sole shared lib", |
| in: ` |
| cc_library { |
| name: "foo", |
| shared_libs: ["libhwbinder"], |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| |
| } |
| `, |
| }, |
| { |
| name: "remove a shared lib", |
| in: ` |
| cc_library { |
| name: "foo", |
| shared_libs: [ |
| "libhwbinder", |
| "libfoo", |
| "libhidltransport", |
| ], |
| } |
| `, |
| out: ` |
| cc_library { |
| name: "foo", |
| shared_libs: [ |
| |
| "libfoo", |
| |
| ], |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, func(fixer *Fixer) error { |
| return removeEmptyLibDependencies(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestRemoveHidlInterfaceTypes(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "remove types", |
| in: ` |
| hidl_interface { |
| name: "foo@1.0", |
| types: ["ParcelFooBar"], |
| } |
| `, |
| out: ` |
| hidl_interface { |
| name: "foo@1.0", |
| |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, func(fixer *Fixer) error { |
| return removeHidlInterfaceTypes(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestRemoveSoongConfigBoolVariable(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "remove bool", |
| in: ` |
| soong_config_module_type { |
| name: "foo", |
| variables: ["bar", "baz"], |
| } |
| |
| soong_config_bool_variable { |
| name: "bar", |
| } |
| |
| soong_config_string_variable { |
| name: "baz", |
| } |
| `, |
| out: ` |
| soong_config_module_type { |
| name: "foo", |
| variables: [ |
| "baz" |
| ], |
| bool_variables: ["bar"], |
| } |
| |
| soong_config_string_variable { |
| name: "baz", |
| } |
| `, |
| }, |
| { |
| name: "existing bool_variables", |
| in: ` |
| soong_config_module_type { |
| name: "foo", |
| variables: ["baz"], |
| bool_variables: ["bar"], |
| } |
| |
| soong_config_bool_variable { |
| name: "baz", |
| } |
| `, |
| out: ` |
| soong_config_module_type { |
| name: "foo", |
| bool_variables: ["bar", "baz"], |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, removeSoongConfigBoolVariable) |
| }) |
| } |
| } |
| |
| func TestRemoveNestedProperty(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| propertyName string |
| }{ |
| { |
| name: "remove no nesting", |
| in: ` |
| cc_library { |
| name: "foo", |
| foo: true, |
| }`, |
| out: ` |
| cc_library { |
| name: "foo", |
| } |
| `, |
| propertyName: "foo", |
| }, |
| { |
| name: "remove one nest", |
| in: ` |
| cc_library { |
| name: "foo", |
| foo: { |
| bar: true, |
| }, |
| }`, |
| out: ` |
| cc_library { |
| name: "foo", |
| } |
| `, |
| propertyName: "foo.bar", |
| }, |
| { |
| name: "remove one nest, multiple props", |
| in: ` |
| cc_library { |
| name: "foo", |
| foo: { |
| bar: true, |
| baz: false, |
| }, |
| }`, |
| out: ` |
| cc_library { |
| name: "foo", |
| foo: { |
| baz: false, |
| }, |
| } |
| `, |
| propertyName: "foo.bar", |
| }, |
| { |
| name: "remove multiple nest", |
| in: ` |
| cc_library { |
| name: "foo", |
| foo: { |
| bar: { |
| baz: { |
| a: true, |
| } |
| }, |
| }, |
| }`, |
| out: ` |
| cc_library { |
| name: "foo", |
| } |
| `, |
| propertyName: "foo.bar.baz.a", |
| }, |
| { |
| name: "remove multiple nest, outer non-empty", |
| in: ` |
| cc_library { |
| name: "foo", |
| foo: { |
| bar: { |
| baz: { |
| a: true, |
| } |
| }, |
| other: true, |
| }, |
| }`, |
| out: ` |
| cc_library { |
| name: "foo", |
| foo: { |
| other: true, |
| }, |
| } |
| `, |
| propertyName: "foo.bar.baz.a", |
| }, |
| { |
| name: "remove multiple nest, inner non-empty", |
| in: ` |
| cc_library { |
| name: "foo", |
| foo: { |
| bar: { |
| baz: { |
| a: true, |
| }, |
| other: true, |
| }, |
| }, |
| }`, |
| out: ` |
| cc_library { |
| name: "foo", |
| foo: { |
| bar: { |
| other: true, |
| }, |
| }, |
| } |
| `, |
| propertyName: "foo.bar.baz.a", |
| }, |
| { |
| name: "remove multiple nest, inner-most non-empty", |
| in: ` |
| cc_library { |
| name: "foo", |
| foo: { |
| bar: { |
| baz: { |
| a: true, |
| other: true, |
| }, |
| }, |
| }, |
| }`, |
| out: ` |
| cc_library { |
| name: "foo", |
| foo: { |
| bar: { |
| baz: { |
| other: true, |
| }, |
| }, |
| }, |
| } |
| `, |
| propertyName: "foo.bar.baz.a", |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, runPatchListMod(removeObsoleteProperty(test.propertyName))) |
| }) |
| } |
| } |
| |
| func TestRemoveObsoleteProperties(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "remove property", |
| in: ` |
| cc_library_shared { |
| name: "foo", |
| product_variables: { |
| other: { |
| bar: true, |
| }, |
| pdk: { |
| enabled: false, |
| }, |
| }, |
| } |
| `, |
| out: ` |
| cc_library_shared { |
| name: "foo", |
| product_variables: { |
| other: { |
| bar: true, |
| }, |
| }, |
| } |
| `, |
| }, |
| { |
| name: "remove property and empty product_variables", |
| in: ` |
| cc_library_shared { |
| name: "foo", |
| product_variables: { |
| pdk: { |
| enabled: false, |
| }, |
| }, |
| } |
| `, |
| out: ` |
| cc_library_shared { |
| name: "foo", |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, runPatchListMod(removeObsoleteProperty("product_variables.pdk"))) |
| }) |
| } |
| } |
| |
| func TestRewriteRuntimeResourceOverlay(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "product_specific runtime_resource_overlay", |
| in: ` |
| runtime_resource_overlay { |
| name: "foo", |
| resource_dirs: ["res"], |
| product_specific: true, |
| } |
| `, |
| out: ` |
| runtime_resource_overlay { |
| name: "foo", |
| resource_dirs: ["res"], |
| product_specific: true, |
| } |
| `, |
| }, |
| { |
| // It's probably wrong for runtime_resource_overlay not to be product specific, but let's not |
| // debate it here. |
| name: "non-product_specific runtime_resource_overlay", |
| in: ` |
| runtime_resource_overlay { |
| name: "foo", |
| resource_dirs: ["res"], |
| product_specific: false, |
| } |
| `, |
| out: ` |
| runtime_resource_overlay { |
| name: "foo", |
| resource_dirs: ["res"], |
| product_specific: false, |
| } |
| `, |
| }, |
| { |
| name: "runtime_resource_overlay without product_specific value", |
| in: ` |
| runtime_resource_overlay { |
| name: "foo", |
| resource_dirs: ["res"], |
| } |
| `, |
| out: ` |
| runtime_resource_overlay { |
| name: "foo", |
| resource_dirs: ["res"], |
| product_specific: true, |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, func(fixer *Fixer) error { |
| return RewriteRuntimeResourceOverlay(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestRewriteTestModuleTypes(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "cc_binary with test_suites", |
| in: ` |
| cc_binary { |
| name: "foo", |
| srcs: ["srcs"], |
| test_suites: ["test_suite1"], |
| } |
| `, |
| out: ` |
| cc_test { |
| name: "foo", |
| srcs: ["srcs"], |
| test_suites: ["test_suite1"], |
| } |
| `, |
| }, |
| { |
| name: "cc_binary without test_suites", |
| in: ` |
| cc_binary { |
| name: "foo", |
| srcs: ["srcs"], |
| } |
| `, |
| out: ` |
| cc_binary { |
| name: "foo", |
| srcs: ["srcs"], |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, func(fixer *Fixer) error { |
| return rewriteTestModuleTypes(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestFormatFlagProperty(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "group options and values for apptflags, dxflags, javacflags, and kotlincflags", |
| in: ` |
| android_test { |
| name: "foo", |
| aaptflags: [ |
| // comment1_1 |
| "--flag1", |
| // comment1_2 |
| "1", |
| // comment2_1 |
| // comment2_2 |
| "--flag2", |
| // comment3_1 |
| // comment3_2 |
| // comment3_3 |
| "--flag3", |
| // comment3_4 |
| // comment3_5 |
| // comment3_6 |
| "3", |
| // other comment1_1 |
| // other comment1_2 |
| ], |
| dxflags: [ |
| "--flag1", |
| // comment1_1 |
| "1", |
| // comment2_1 |
| "--flag2", |
| // comment3_1 |
| "--flag3", |
| // comment3_2 |
| "3", |
| ], |
| javacflags: [ |
| "--flag1", |
| |
| "1", |
| "--flag2", |
| "--flag3", |
| "3", |
| ], |
| kotlincflags: [ |
| |
| "--flag1", |
| "1", |
| |
| "--flag2", |
| "--flag3", |
| "3", |
| |
| ], |
| } |
| `, |
| out: ` |
| android_test { |
| name: "foo", |
| aaptflags: [ |
| // comment1_1 |
| // comment1_2 |
| "--flag1 1", |
| // comment2_1 |
| // comment2_2 |
| "--flag2", |
| // comment3_1 |
| // comment3_2 |
| // comment3_3 |
| // comment3_4 |
| // comment3_5 |
| // comment3_6 |
| "--flag3 3", |
| // other comment1_1 |
| // other comment1_2 |
| ], |
| dxflags: [ |
| // comment1_1 |
| "--flag1 1", |
| // comment2_1 |
| "--flag2", |
| // comment3_1 |
| // comment3_2 |
| "--flag3 3", |
| ], |
| javacflags: [ |
| |
| "--flag1 1", |
| "--flag2", |
| "--flag3 3", |
| ], |
| kotlincflags: [ |
| |
| "--flag1 1", |
| |
| "--flag2", |
| "--flag3 3", |
| |
| ], |
| } |
| `, |
| }, |
| { |
| name: "group options and values for asflags, cflags, clang_asflags, clang_cflags, conlyflags, cppflags, ldflags, and tidy_flags", |
| in: ` |
| cc_test { |
| name: "foo", |
| asflags: [ |
| // comment1_1 |
| "--flag1", |
| "1", |
| // comment2_1 |
| // comment2_2 |
| "--flag2", |
| // comment2_3 |
| "2", |
| // comment3_1 |
| // comment3_2 |
| "--flag3", |
| // comment3_3 |
| // comment3_4 |
| // comment3_4 |
| "3", |
| // comment4_1 |
| // comment4_2 |
| // comment4_3 |
| "--flag4", |
| ], |
| cflags: [ |
| "--flag1", |
| "1", |
| "--flag2", |
| "2", |
| "--flag3", |
| "3", |
| "--flag4", |
| ], |
| clang_asflags: [ |
| "--flag1", |
| "1", |
| "--flag2", |
| "2", |
| "--flag3", |
| "3", |
| "--flag4", |
| ], |
| clang_cflags: [ |
| "--flag1", |
| "1", |
| "--flag2", |
| "2", |
| "--flag3", |
| "3", |
| "--flag4", |
| ], |
| conlyflags: [ |
| "--flag1", |
| "1", |
| "--flag2", |
| "2", |
| "--flag3", |
| "3", |
| "--flag4", |
| ], |
| cppflags: [ |
| "--flag1", |
| "1", |
| "--flag2", |
| "2", |
| "--flag3", |
| "3", |
| "--flag4", |
| ], |
| ldflags: [ |
| "--flag1", |
| "1", |
| "--flag2", |
| "2", |
| "--flag3", |
| "3", |
| "--flag4", |
| ], |
| tidy_flags: [ |
| "--flag1", |
| "1", |
| "--flag2", |
| "2", |
| "--flag3", |
| "3", |
| "--flag4", |
| ], |
| } |
| `, |
| out: ` |
| cc_test { |
| name: "foo", |
| asflags: [ |
| // comment1_1 |
| "--flag1 1", |
| // comment2_1 |
| // comment2_2 |
| // comment2_3 |
| "--flag2 2", |
| // comment3_1 |
| // comment3_2 |
| // comment3_3 |
| // comment3_4 |
| // comment3_4 |
| "--flag3 3", |
| // comment4_1 |
| // comment4_2 |
| // comment4_3 |
| "--flag4", |
| ], |
| cflags: [ |
| "--flag1 1", |
| "--flag2 2", |
| "--flag3 3", |
| "--flag4", |
| ], |
| clang_asflags: [ |
| "--flag1 1", |
| "--flag2 2", |
| "--flag3 3", |
| "--flag4", |
| ], |
| clang_cflags: [ |
| "--flag1 1", |
| "--flag2 2", |
| "--flag3 3", |
| "--flag4", |
| ], |
| conlyflags: [ |
| "--flag1 1", |
| "--flag2 2", |
| "--flag3 3", |
| "--flag4", |
| ], |
| cppflags: [ |
| "--flag1 1", |
| "--flag2 2", |
| "--flag3 3", |
| "--flag4", |
| ], |
| ldflags: [ |
| "--flag1 1", |
| "--flag2 2", |
| "--flag3 3", |
| "--flag4", |
| ], |
| tidy_flags: [ |
| "--flag1 1", |
| "--flag2 2", |
| "--flag3 3", |
| "--flag4", |
| ], |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPass(t, test.in, test.out, runPatchListMod(formatFlagProperties)) |
| }) |
| } |
| } |
| |
| func TestRewriteLicenseProperty(t *testing.T) { |
| mockFs := pathtools.MockFs(map[string][]byte{ |
| "a/b/c/d/Android.mk": []byte("this is not important."), |
| "a/b/LicenseFile1": []byte("LicenseFile1"), |
| "a/b/LicenseFile2": []byte("LicenseFile2"), |
| "a/b/Android.bp": []byte("license {\n\tname: \"reuse_a_b_license\",\n}\n"), |
| }) |
| relativePath := "a/b/c/d" |
| relativePathErr := "a/b/c" |
| tests := []struct { |
| name string |
| in string |
| fs pathtools.FileSystem |
| path string |
| out string |
| }{ |
| { |
| name: "license rewriting with one module", |
| in: ` |
| android_test { |
| name: "foo", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| } |
| `, |
| out: ` |
| package { |
| // See: http://go/android-license-faq |
| default_applicable_licenses: [ |
| "Android-Apache-2.0", |
| ], |
| } |
| |
| android_test { |
| name: "foo", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| } |
| `, |
| }, |
| { |
| name: "license rewriting with two modules", |
| in: ` |
| android_test { |
| name: "foo1", |
| android_license_kinds: ["license_kind1"], |
| android_license_conditions: ["license_notice1"], |
| } |
| |
| android_test { |
| name: "foo2", |
| android_license_kinds: ["license_kind2"], |
| android_license_conditions: ["license_notice2"], |
| } |
| `, |
| out: ` |
| package { |
| // See: http://go/android-license-faq |
| default_applicable_licenses: [ |
| "Android-Apache-2.0", |
| ], |
| } |
| |
| android_test { |
| name: "foo1", |
| android_license_kinds: ["license_kind1"], |
| android_license_conditions: ["license_notice1"], |
| } |
| |
| android_test { |
| name: "foo2", |
| android_license_kinds: ["license_kind2"], |
| android_license_conditions: ["license_notice2"], |
| } |
| `, |
| }, |
| { |
| name: "license rewriting with license files in the current directory", |
| in: ` |
| android_test { |
| name: "foo", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| android_license_files: ["LicenseFile1", "LicenseFile2",], |
| } |
| `, |
| fs: mockFs, |
| path: relativePath, |
| out: ` |
| package { |
| // See: http://go/android-license-faq |
| default_applicable_licenses: [ |
| "a_b_c_d_license", |
| ], |
| } |
| |
| license { |
| name: "a_b_c_d_license", |
| visibility: [":__subpackages__"], |
| license_kinds: [ |
| "license_kind", |
| ], |
| license_text: [ |
| "LicenseFile1", |
| "LicenseFile2", |
| ], |
| } |
| |
| android_test { |
| name: "foo", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| android_license_files: [ |
| "LicenseFile1", |
| "LicenseFile2", |
| ], |
| } |
| `, |
| }, |
| { |
| name: "license rewriting with license files outside the current directory", |
| in: ` |
| android_test { |
| name: "foo", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| android_license_files: ["../../LicenseFile1", "../../LicenseFile2",], |
| } |
| `, |
| fs: mockFs, |
| path: relativePath, |
| out: ` |
| package { |
| // See: http://go/android-license-faq |
| default_applicable_licenses: [ |
| "reuse_a_b_license", |
| ], |
| } |
| |
| android_test { |
| name: "foo", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| android_license_files: [ |
| "../../LicenseFile1", |
| "../../LicenseFile2", |
| ], |
| } |
| `, |
| }, |
| { |
| name: "license rewriting with no Android.bp file in the expected location", |
| in: ` |
| android_test { |
| name: "foo", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| android_license_files: ["../../LicenseFile1", "../../LicenseFile2",], |
| } |
| `, |
| fs: pathtools.MockFs(map[string][]byte{ |
| "a/b/c/d/Android.mk": []byte("this is not important."), |
| "a/b/LicenseFile1": []byte("LicenseFile1"), |
| "a/b/LicenseFile2": []byte("LicenseFile2"), |
| "a/Android.bp": []byte("license {\n\tname: \"reuse_a_b_license\",\n}\n"), |
| }), |
| path: relativePath, |
| out: ` |
| // Error: No Android.bp file is found at path |
| // a/b |
| // Please add one there with the needed license module first. |
| // Then reset the default_applicable_licenses property below with the license module name. |
| package { |
| // See: http://go/android-license-faq |
| default_applicable_licenses: [ |
| "", |
| ], |
| } |
| |
| android_test { |
| name: "foo", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| android_license_files: [ |
| "../../LicenseFile1", |
| "../../LicenseFile2", |
| ], |
| } |
| `, |
| }, |
| { |
| name: "license rewriting with an Android.bp file without a license module", |
| in: ` |
| android_test { |
| name: "foo", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| android_license_files: ["../../LicenseFile1", "../../LicenseFile2",], |
| } |
| `, |
| fs: pathtools.MockFs(map[string][]byte{ |
| "a/b/c/d/Android.mk": []byte("this is not important."), |
| "a/b/LicenseFile1": []byte("LicenseFile1"), |
| "a/b/LicenseFile2": []byte("LicenseFile2"), |
| "a/b/Android.bp": []byte("non_license {\n\tname: \"reuse_a_b_license\",\n}\n"), |
| }), |
| path: relativePath, |
| out: ` |
| // Error: Cannot get the name of the license module in the |
| // a/b/Android.bp file. |
| // If no such license module exists, please add one there first. |
| // Then reset the default_applicable_licenses property below with the license module name. |
| package { |
| // See: http://go/android-license-faq |
| default_applicable_licenses: [ |
| "", |
| ], |
| } |
| |
| android_test { |
| name: "foo", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| android_license_files: [ |
| "../../LicenseFile1", |
| "../../LicenseFile2", |
| ], |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPassOnce(t, test.in, test.out, runPatchListMod(rewriteLicenseProperty(test.fs, test.path))) |
| }) |
| } |
| |
| testErrs := []struct { |
| name string |
| in string |
| fs pathtools.FileSystem |
| path string |
| expectedErr string |
| }{ |
| { |
| name: "license rewriting with a wrong path", |
| in: ` |
| android_test { |
| name: "foo", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| android_license_files: ["../../LicenseFile1", "../../LicenseFile2",], |
| } |
| `, |
| fs: mockFs, |
| path: relativePathErr, |
| expectedErr: ` |
| Cannot find an Android.mk file at path "a/b/c" |
| `, |
| }, |
| } |
| for _, test := range testErrs { |
| t.Run(test.name, func(t *testing.T) { |
| checkError(t, test.in, test.expectedErr, runPatchListMod(rewriteLicenseProperty(test.fs, test.path))) |
| }) |
| } |
| } |
| |
| func TestHaveSameLicense(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "two modules with the same license", |
| in: ` |
| android_test { |
| name: "foo1", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| } |
| |
| android_test { |
| name: "foo2", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| } |
| `, |
| out: ` |
| android_test { |
| name: "foo1", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| } |
| |
| android_test { |
| name: "foo2", |
| android_license_kinds: ["license_kind"], |
| android_license_conditions: ["license_notice"], |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPassOnce(t, test.in, test.out, func(fixer *Fixer) error { |
| return haveSameLicense(fixer) |
| }) |
| }) |
| } |
| testErrs := []struct { |
| name string |
| in string |
| expectedErr string |
| }{ |
| { |
| name: "two modules will different licenses", |
| in: ` |
| android_test { |
| name: "foo1", |
| android_license_kinds: ["license_kind1"], |
| android_license_conditions: ["license_notice1"], |
| } |
| |
| android_test { |
| name: "foo2", |
| android_license_kinds: ["license_kind2"], |
| android_license_conditions: ["license_notice2"], |
| } |
| `, |
| expectedErr: ` |
| Modules foo1 and foo2 are expected to have the same android_license_kinds property. |
| `, |
| }, |
| } |
| for _, test := range testErrs { |
| t.Run(test.name, func(t *testing.T) { |
| checkError(t, test.in, test.expectedErr, func(fixer *Fixer) error { |
| return haveSameLicense(fixer) |
| }) |
| }) |
| } |
| } |
| |
| func TestRemoveResourceAndAssetsIfDefault(t *testing.T) { |
| tests := []struct { |
| name string |
| in string |
| out string |
| }{ |
| { |
| name: "resource_dirs default", |
| in: ` |
| android_app { |
| name: "foo", |
| resource_dirs: ["res"], |
| } |
| `, |
| out: ` |
| android_app { |
| name: "foo", |
| |
| } |
| `, |
| }, |
| { |
| name: "resource_dirs not default", |
| in: ` |
| android_app { |
| name: "foo", |
| resource_dirs: ["reso"], |
| } |
| `, |
| out: ` |
| android_app { |
| name: "foo", |
| resource_dirs: ["reso"], |
| } |
| `, |
| }, |
| { |
| name: "resource_dirs includes not default", |
| in: ` |
| android_app { |
| name: "foo", |
| resource_dirs: ["res", "reso"], |
| } |
| `, |
| out: ` |
| android_app { |
| name: "foo", |
| resource_dirs: ["res", "reso"], |
| } |
| `, |
| }, { |
| name: "asset_dirs default", |
| in: ` |
| android_app { |
| name: "foo", |
| asset_dirs: ["assets"], |
| } |
| `, |
| out: ` |
| android_app { |
| name: "foo", |
| |
| } |
| `, |
| }, |
| { |
| name: "asset_dirs not default", |
| in: ` |
| android_app { |
| name: "foo", |
| asset_dirs: ["assety"], |
| } |
| `, |
| out: ` |
| android_app { |
| name: "foo", |
| asset_dirs: ["assety"], |
| } |
| `, |
| }, |
| { |
| name: "asset_dirs includes not default", |
| in: ` |
| android_app { |
| name: "foo", |
| asset_dirs: ["assets", "assety"], |
| } |
| `, |
| out: ` |
| android_app { |
| name: "foo", |
| asset_dirs: ["assets", "assety"], |
| } |
| `, |
| }, |
| { |
| name: "resource_dirs and asset_dirs both default", |
| in: ` |
| android_app { |
| name: "foo", |
| asset_dirs: ["assets"], |
| resource_dirs: ["res"], |
| } |
| `, |
| out: ` |
| android_app { |
| name: "foo", |
| |
| } |
| `, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| runPassOnce(t, test.in, test.out, func(fixer *Fixer) error { |
| return removeResourceAndAssetsIfDefault(fixer) |
| }) |
| }) |
| } |
| } |