1// Copyright 2019 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 15package blueprint 16 17import ( 18 "reflect" 19 "strings" 20 "testing" 21) 22 23type moduleCtxTestModule struct { 24 SimpleName 25} 26 27func newModuleCtxTestModule() (Module, []interface{}) { 28 m := &moduleCtxTestModule{} 29 return m, []interface{}{&m.SimpleName.Properties} 30} 31 32func (f *moduleCtxTestModule) GenerateBuildActions(ModuleContext) { 33} 34 35func addVariantDepsResultMutator(variants []Variation, tag DependencyTag, from, to string, results map[string][]Module) func(ctx BottomUpMutatorContext) { 36 return func(ctx BottomUpMutatorContext) { 37 if ctx.ModuleName() == from { 38 ret := ctx.AddVariationDependencies(variants, tag, to) 39 results[ctx.ModuleName()] = ret 40 } 41 } 42} 43 44func expectedErrors(t *testing.T, errs []error, expectedMessages ...string) { 45 t.Helper() 46 if len(errs) != len(expectedMessages) { 47 t.Errorf("expected %d error, found: %q", len(expectedMessages), errs) 48 } else { 49 for i, expected := range expectedMessages { 50 err := errs[i] 51 if err.Error() != expected { 52 t.Errorf("expected error %q found %q", expected, err) 53 } 54 } 55 } 56} 57 58func TestAddVariationDependencies(t *testing.T) { 59 runWithFailures := func(ctx *Context, expectedErr string) { 60 t.Helper() 61 bp := ` 62 test { 63 name: "foo", 64 } 65 66 test { 67 name: "bar", 68 } 69 ` 70 71 mockFS := map[string][]byte{ 72 "Android.bp": []byte(bp), 73 } 74 75 ctx.MockFileSystem(mockFS) 76 77 _, errs := ctx.ParseFileList(".", []string{"Android.bp"}, nil) 78 if len(errs) > 0 { 79 t.Errorf("unexpected parse errors:") 80 for _, err := range errs { 81 t.Errorf(" %s", err) 82 } 83 } 84 85 _, errs = ctx.ResolveDependencies(nil) 86 if len(errs) > 0 { 87 if expectedErr == "" { 88 t.Errorf("unexpected dep errors:") 89 for _, err := range errs { 90 t.Errorf(" %s", err) 91 } 92 } else { 93 for _, err := range errs { 94 if strings.Contains(err.Error(), expectedErr) { 95 continue 96 } else { 97 t.Errorf("unexpected dep error: %s", err) 98 } 99 } 100 } 101 } else if expectedErr != "" { 102 t.Errorf("missing dep error: %s", expectedErr) 103 } 104 } 105 106 run := func(ctx *Context) { 107 t.Helper() 108 runWithFailures(ctx, "") 109 } 110 111 t.Run("parallel", func(t *testing.T) { 112 ctx := NewContext() 113 ctx.RegisterModuleType("test", newModuleCtxTestModule) 114 results := make(map[string][]Module) 115 depsMutator := addVariantDepsResultMutator(nil, nil, "foo", "bar", results) 116 ctx.RegisterBottomUpMutator("deps", depsMutator) 117 118 run(ctx) 119 120 foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") 121 bar := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("") 122 123 if g, w := foo.forwardDeps, []*moduleInfo{bar}; !reflect.DeepEqual(g, w) { 124 t.Fatalf("expected foo deps to be %q, got %q", w, g) 125 } 126 127 if g, w := results["foo"], []Module{bar.logicModule}; !reflect.DeepEqual(g, w) { 128 t.Fatalf("expected AddVariationDependencies return value to be %q, got %q", w, g) 129 } 130 }) 131 132 t.Run("missing", func(t *testing.T) { 133 ctx := NewContext() 134 ctx.RegisterModuleType("test", newModuleCtxTestModule) 135 results := make(map[string][]Module) 136 depsMutator := addVariantDepsResultMutator(nil, nil, "foo", "baz", results) 137 ctx.RegisterBottomUpMutator("deps", depsMutator) 138 runWithFailures(ctx, `"foo" depends on undefined module "baz"`) 139 140 foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") 141 142 if g, w := foo.forwardDeps, []*moduleInfo(nil); !reflect.DeepEqual(g, w) { 143 t.Fatalf("expected foo deps to be %q, got %q", w, g) 144 } 145 146 if g, w := results["foo"], []Module{nil}; !reflect.DeepEqual(g, w) { 147 t.Fatalf("expected AddVariationDependencies return value to be %q, got %q", w, g) 148 } 149 }) 150 151 t.Run("allow missing", func(t *testing.T) { 152 ctx := NewContext() 153 ctx.SetAllowMissingDependencies(true) 154 ctx.RegisterModuleType("test", newModuleCtxTestModule) 155 results := make(map[string][]Module) 156 depsMutator := addVariantDepsResultMutator(nil, nil, "foo", "baz", results) 157 ctx.RegisterBottomUpMutator("deps", depsMutator) 158 run(ctx) 159 160 foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") 161 162 if g, w := foo.forwardDeps, []*moduleInfo(nil); !reflect.DeepEqual(g, w) { 163 t.Fatalf("expected foo deps to be %q, got %q", w, g) 164 } 165 166 if g, w := results["foo"], []Module{nil}; !reflect.DeepEqual(g, w) { 167 t.Fatalf("expected AddVariationDependencies return value to be %q, got %q", w, g) 168 } 169 }) 170 171} 172 173func TestInvalidModuleNames(t *testing.T) { 174 t.Helper() 175 bp := ` 176 test { 177 name: "fo o", // contains space 178 } 179 ` 180 181 mockFS := map[string][]byte{ 182 "Android.bp": []byte(bp), 183 } 184 185 ctx := NewContext() 186 ctx.RegisterModuleType("test", newModuleCtxTestModule) 187 188 ctx.MockFileSystem(mockFS) 189 _, errs := ctx.ParseFileList(".", []string{"Android.bp"}, nil) 190 191 if len(errs) != 1 || !strings.Contains(errs[0].Error(), "should use a valid name") { 192 t.Errorf("Expected invalid name exception, found %s", errs) 193 } 194} 195 196func TestCheckBlueprintSyntax(t *testing.T) { 197 factories := map[string]ModuleFactory{ 198 "test": newModuleCtxTestModule, 199 } 200 201 t.Run("valid", func(t *testing.T) { 202 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 203test { 204 name: "test", 205} 206`) 207 expectedErrors(t, errs) 208 }) 209 210 t.Run("syntax error", func(t *testing.T) { 211 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 212test { 213 name: "test", 214 215`) 216 217 expectedErrors(t, errs, `path/Blueprint:5:1: expected "}", found EOF`) 218 }) 219 220 t.Run("unknown module type", func(t *testing.T) { 221 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 222test2 { 223 name: "test", 224} 225`) 226 227 expectedErrors(t, errs, `path/Blueprint:2:1: unrecognized module type "test2"`) 228 }) 229 230 t.Run("unknown property name", func(t *testing.T) { 231 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 232test { 233 nam: "test", 234} 235`) 236 237 expectedErrors(t, errs, `path/Blueprint:3:5: unrecognized property "nam"`) 238 }) 239 240 t.Run("invalid property type", func(t *testing.T) { 241 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 242test { 243 name: false, 244} 245`) 246 247 expectedErrors(t, errs, `path/Blueprint:3:8: can't assign bool value to string property "name"`) 248 }) 249 250 t.Run("multiple failures", func(t *testing.T) { 251 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 252test { 253 name: false, 254} 255 256test2 { 257 name: false, 258} 259`) 260 261 expectedErrors(t, errs, 262 `path/Blueprint:3:8: can't assign bool value to string property "name"`, 263 `path/Blueprint:6:1: unrecognized module type "test2"`, 264 ) 265 }) 266} 267 268type addNinjaDepsTestModule struct { 269 SimpleName 270} 271 272func addNinjaDepsTestModuleFactory() (Module, []interface{}) { 273 module := &addNinjaDepsTestModule{} 274 AddLoadHook(module, func(ctx LoadHookContext) { 275 ctx.AddNinjaFileDeps("LoadHookContext") 276 }) 277 return module, []interface{}{&module.SimpleName.Properties} 278} 279 280func (m *addNinjaDepsTestModule) GenerateBuildActions(ctx ModuleContext) { 281 ctx.AddNinjaFileDeps("GenerateBuildActions") 282} 283 284func addNinjaDepsTestBottomUpMutator(ctx BottomUpMutatorContext) { 285 ctx.AddNinjaFileDeps("BottomUpMutator") 286} 287 288type addNinjaDepsTestSingleton struct{} 289 290func addNinjaDepsTestSingletonFactory() Singleton { 291 return &addNinjaDepsTestSingleton{} 292} 293 294func (s *addNinjaDepsTestSingleton) GenerateBuildActions(ctx SingletonContext) { 295 ctx.AddNinjaFileDeps("Singleton") 296} 297 298func TestAddNinjaFileDeps(t *testing.T) { 299 ctx := NewContext() 300 ctx.MockFileSystem(map[string][]byte{ 301 "Android.bp": []byte(` 302 test { 303 name: "test", 304 } 305 `), 306 }) 307 308 ctx.RegisterModuleType("test", addNinjaDepsTestModuleFactory) 309 ctx.RegisterBottomUpMutator("testBottomUpMutator", addNinjaDepsTestBottomUpMutator) 310 ctx.RegisterSingletonType("testSingleton", addNinjaDepsTestSingletonFactory, false) 311 parseDeps, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) 312 if len(errs) > 0 { 313 t.Errorf("unexpected parse errors:") 314 for _, err := range errs { 315 t.Errorf(" %s", err) 316 } 317 t.FailNow() 318 } 319 320 resolveDeps, errs := ctx.ResolveDependencies(nil) 321 if len(errs) > 0 { 322 t.Errorf("unexpected dep errors:") 323 for _, err := range errs { 324 t.Errorf(" %s", err) 325 } 326 t.FailNow() 327 } 328 329 prepareDeps, errs := ctx.PrepareBuildActions(nil) 330 if len(errs) > 0 { 331 t.Errorf("unexpected prepare errors:") 332 for _, err := range errs { 333 t.Errorf(" %s", err) 334 } 335 t.FailNow() 336 } 337 338 if g, w := parseDeps, []string{"Android.bp", "LoadHookContext"}; !reflect.DeepEqual(g, w) { 339 t.Errorf("ParseBlueprintsFiles: wanted deps %q, got %q", w, g) 340 } 341 342 if g, w := resolveDeps, []string{"BottomUpMutator"}; !reflect.DeepEqual(g, w) { 343 t.Errorf("ResolveDependencies: wanted deps %q, got %q", w, g) 344 } 345 346 if g, w := prepareDeps, []string{"GenerateBuildActions", "Singleton"}; !reflect.DeepEqual(g, w) { 347 t.Errorf("PrepareBuildActions: wanted deps %q, got %q", w, g) 348 } 349 350} 351