Home | History | Annotate | Download | only in bpfix
      1 // Copyright 2017 Google Inc. All rights reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 // This file implements the logic of bpfix and also provides a programmatic interface
     16 
     17 package bpfix
     18 
     19 import (
     20 	"bytes"
     21 	"fmt"
     22 	"strings"
     23 	"testing"
     24 
     25 	"reflect"
     26 
     27 	"github.com/google/blueprint/parser"
     28 )
     29 
     30 // TODO(jeffrygaston) remove this when position is removed from ParseNode (in b/38325146) and we can directly do reflect.DeepEqual
     31 func printListOfStrings(items []string) (text string) {
     32 	if len(items) == 0 {
     33 		return "[]"
     34 	}
     35 	return fmt.Sprintf("[\"%s\"]", strings.Join(items, "\", \""))
     36 
     37 }
     38 
     39 func buildTree(local_include_dirs []string, export_include_dirs []string) (file *parser.File, errs []error) {
     40 	// TODO(jeffrygaston) use the builder class when b/38325146 is done
     41 	input := fmt.Sprintf(`cc_library_shared {
     42 	    name: "iAmAModule",
     43 	    local_include_dirs: %s,
     44 	    export_include_dirs: %s,
     45 	}
     46 	`,
     47 		printListOfStrings(local_include_dirs), printListOfStrings(export_include_dirs))
     48 	tree, errs := parser.Parse("", strings.NewReader(input), parser.NewScope(nil))
     49 	if len(errs) > 0 {
     50 		errs = append([]error{fmt.Errorf("failed to parse:\n%s", input)}, errs...)
     51 	}
     52 	return tree, errs
     53 }
     54 
     55 func implFilterListTest(t *testing.T, local_include_dirs []string, export_include_dirs []string, expectedResult []string) {
     56 	// build tree
     57 	tree, errs := buildTree(local_include_dirs, export_include_dirs)
     58 	if len(errs) > 0 {
     59 		t.Error("failed to build tree")
     60 		for _, err := range errs {
     61 			t.Error(err)
     62 		}
     63 		t.Fatalf("%d parse errors", len(errs))
     64 	}
     65 
     66 	fixer := NewFixer(tree)
     67 
     68 	// apply simplifications
     69 	err := fixer.simplifyKnownPropertiesDuplicatingEachOther()
     70 	if len(errs) > 0 {
     71 		t.Fatal(err)
     72 	}
     73 
     74 	// lookup legacy property
     75 	mod := fixer.tree.Defs[0].(*parser.Module)
     76 	_, found := mod.GetProperty("local_include_dirs")
     77 	if !found {
     78 		t.Fatalf("failed to include key local_include_dirs in parse tree")
     79 	}
     80 
     81 	// check that the value for the legacy property was updated to the correct value
     82 	errorHeader := fmt.Sprintf("\nFailed to correctly simplify key 'local_include_dirs' in the presence of 'export_include_dirs.'\n"+
     83 		"original local_include_dirs: %q\n"+
     84 		"original export_include_dirs: %q\n"+
     85 		"expected result: %q\n"+
     86 		"actual result: ",
     87 		local_include_dirs, export_include_dirs, expectedResult)
     88 	result, ok := mod.GetProperty("local_include_dirs")
     89 	if !ok {
     90 		t.Fatal(errorHeader + "property not found")
     91 	}
     92 
     93 	listResult, ok := result.Value.(*parser.List)
     94 	if !ok {
     95 		t.Fatalf("%sproperty is not a list: %v", errorHeader, listResult)
     96 	}
     97 
     98 	actualExpressions := listResult.Values
     99 	actualValues := make([]string, 0)
    100 	for _, expr := range actualExpressions {
    101 		str := expr.(*parser.String)
    102 		actualValues = append(actualValues, str.Value)
    103 	}
    104 
    105 	if !reflect.DeepEqual(actualValues, expectedResult) {
    106 		t.Fatalf("%s%q\nlists are different", errorHeader, actualValues)
    107 	}
    108 }
    109 
    110 func TestSimplifyKnownVariablesDuplicatingEachOther(t *testing.T) {
    111 	// TODO use []Expression{} once buildTree above can support it (which is after b/38325146 is done)
    112 	implFilterListTest(t, []string{"include"}, []string{"include"}, []string{})
    113 	implFilterListTest(t, []string{"include1"}, []string{"include2"}, []string{"include1"})
    114 	implFilterListTest(t, []string{"include1", "include2", "include3", "include4"}, []string{"include2"},
    115 		[]string{"include1", "include3", "include4"})
    116 	implFilterListTest(t, []string{}, []string{"include"}, []string{})
    117 	implFilterListTest(t, []string{}, []string{}, []string{})
    118 }
    119 
    120 func TestMergeMatchingProperties(t *testing.T) {
    121 	tests := []struct {
    122 		name string
    123 		in   string
    124 		out  string
    125 	}{
    126 		{
    127 			name: "empty",
    128 			in: `
    129 				java_library {
    130 					name: "foo",
    131 					static_libs: [],
    132 					static_libs: [],
    133 				}
    134 			`,
    135 			out: `
    136 				java_library {
    137 					name: "foo",
    138 					static_libs: [],
    139 				}
    140 			`,
    141 		},
    142 		{
    143 			name: "single line into multiline",
    144 			in: `
    145 				java_library {
    146 					name: "foo",
    147 					static_libs: [
    148 						"a",
    149 						"b",
    150 					],
    151 					//c1
    152 					static_libs: ["c" /*c2*/],
    153 				}
    154 			`,
    155 			out: `
    156 				java_library {
    157 					name: "foo",
    158 					static_libs: [
    159 						"a",
    160 						"b",
    161 						"c", /*c2*/
    162 					],
    163 					//c1
    164 				}
    165 			`,
    166 		},
    167 		{
    168 			name: "multiline into multiline",
    169 			in: `
    170 				java_library {
    171 					name: "foo",
    172 					static_libs: [
    173 						"a",
    174 						"b",
    175 					],
    176 					//c1
    177 					static_libs: [
    178 						//c2
    179 						"c", //c3
    180 						"d",
    181 					],
    182 				}
    183 			`,
    184 			out: `
    185 				java_library {
    186 					name: "foo",
    187 					static_libs: [
    188 						"a",
    189 						"b",
    190 						//c2
    191 						"c", //c3
    192 						"d",
    193 					],
    194 					//c1
    195 				}
    196 			`,
    197 		},
    198 	}
    199 
    200 	for _, test := range tests {
    201 		t.Run(test.name, func(t *testing.T) {
    202 			expected, err := Reformat(test.out)
    203 			if err != nil {
    204 				t.Error(err)
    205 			}
    206 
    207 			in, err := Reformat(test.in)
    208 			if err != nil {
    209 				t.Error(err)
    210 			}
    211 
    212 			tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil))
    213 			if errs != nil {
    214 				t.Fatal(errs)
    215 			}
    216 
    217 			fixer := NewFixer(tree)
    218 
    219 			got := ""
    220 			prev := "foo"
    221 			passes := 0
    222 			for got != prev && passes < 10 {
    223 				err := fixer.mergeMatchingModuleProperties()
    224 				if err != nil {
    225 					t.Fatal(err)
    226 				}
    227 
    228 				out, err := parser.Print(fixer.tree)
    229 				if err != nil {
    230 					t.Fatal(err)
    231 				}
    232 
    233 				prev = got
    234 				got = string(out)
    235 				passes++
    236 			}
    237 
    238 			if got != expected {
    239 				t.Errorf("failed testcase '%s'\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n",
    240 					test.name, in, expected, got)
    241 			}
    242 
    243 		})
    244 	}
    245 }
    246