Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/soong/bpfix/bpfix/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 38 kB image not shown  

Quelle  bpfix_test.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// 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"
 "os"
 "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))
 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))
 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"],
    }
   `,
  },
  {
   name: "android_app with android_test",
   in: `
    android_app {
     name: "foo",
     srcs: ["srcs"],
     test_suites: ["test_suite1"],
    }
   `,
   out: `
    android_test {
     name: "foo",
     srcs: ["srcs"],
     test_suites: ["test_suite1"],
    }
   `,
  },
  {
   name: "android_app without test_suites",
   in: `
    android_app {
     name: "foo",
     srcs: ["srcs"],
    }
   `,
   out: `
    android_app {
     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)
   })
  })
 }
}

func TestMain(m *testing.M) {
 // Skip checking Android.mk path with cleaning "ANDROID_BUILD_TOP"
 os.Setenv("ANDROID_BUILD_TOP", "")
 os.Exit(m.Run())
}

[Dauer der Verarbeitung: 0.12 Sekunden, vorverarbeitet 2026-06-28]