github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/blueprint/context_test.go (about) 1 // Copyright 2014 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 package blueprint 16 17 import ( 18 "bytes" 19 "errors" 20 "fmt" 21 "reflect" 22 "strings" 23 "sync" 24 "testing" 25 "time" 26 27 "github.com/google/blueprint/parser" 28 ) 29 30 type Walker interface { 31 Walk() bool 32 } 33 34 type fooModule struct { 35 SimpleName 36 properties struct { 37 Deps []string 38 Foo string 39 } 40 } 41 42 func newFooModule() (Module, []interface{}) { 43 m := &fooModule{} 44 return m, []interface{}{&m.properties, &m.SimpleName.Properties} 45 } 46 47 func (f *fooModule) GenerateBuildActions(ModuleContext) { 48 } 49 50 func (f *fooModule) DynamicDependencies(ctx DynamicDependerModuleContext) []string { 51 return f.properties.Deps 52 } 53 54 func (f *fooModule) Foo() string { 55 return f.properties.Foo 56 } 57 58 func (f *fooModule) Walk() bool { 59 return true 60 } 61 62 type barModule struct { 63 SimpleName 64 properties struct { 65 Deps []string 66 Bar bool 67 } 68 } 69 70 func newBarModule() (Module, []interface{}) { 71 m := &barModule{} 72 return m, []interface{}{&m.properties, &m.SimpleName.Properties} 73 } 74 75 func (b *barModule) DynamicDependencies(ctx DynamicDependerModuleContext) []string { 76 return b.properties.Deps 77 } 78 79 func (b *barModule) GenerateBuildActions(ModuleContext) { 80 } 81 82 func (b *barModule) Bar() bool { 83 return b.properties.Bar 84 } 85 86 func (b *barModule) Walk() bool { 87 return false 88 } 89 90 func TestContextParse(t *testing.T) { 91 ctx := NewContext() 92 ctx.RegisterModuleType("foo_module", newFooModule) 93 ctx.RegisterModuleType("bar_module", newBarModule) 94 95 r := bytes.NewBufferString(` 96 foo_module { 97 name: "MyFooModule", 98 deps: ["MyBarModule"], 99 } 100 101 bar_module { 102 name: "MyBarModule", 103 } 104 `) 105 106 _, _, errs := ctx.parseOne(".", "Blueprint", r, parser.NewScope(nil), nil) 107 if len(errs) > 0 { 108 t.Errorf("unexpected parse errors:") 109 for _, err := range errs { 110 t.Errorf(" %s", err) 111 } 112 t.FailNow() 113 } 114 115 _, errs = ctx.ResolveDependencies(nil) 116 if len(errs) > 0 { 117 t.Errorf("unexpected dep errors:") 118 for _, err := range errs { 119 t.Errorf(" %s", err) 120 } 121 t.FailNow() 122 } 123 } 124 125 // |---B===D - represents a non-walkable edge 126 // A = represents a walkable edge 127 // |===C---E===G 128 // | | A should not be visited because it's the root node. 129 // |===F===| B, D and E should not be walked. 130 func TestWalkDeps(t *testing.T) { 131 ctx := NewContext() 132 ctx.MockFileSystem(map[string][]byte{ 133 "Blueprints": []byte(` 134 foo_module { 135 name: "A", 136 deps: ["B", "C"], 137 } 138 139 bar_module { 140 name: "B", 141 deps: ["D"], 142 } 143 144 foo_module { 145 name: "C", 146 deps: ["E", "F"], 147 } 148 149 foo_module { 150 name: "D", 151 } 152 153 bar_module { 154 name: "E", 155 deps: ["G"], 156 } 157 158 foo_module { 159 name: "F", 160 deps: ["G"], 161 } 162 163 foo_module { 164 name: "G", 165 } 166 `), 167 }) 168 169 ctx.RegisterModuleType("foo_module", newFooModule) 170 ctx.RegisterModuleType("bar_module", newBarModule) 171 _, errs := ctx.ParseBlueprintsFiles("Blueprints") 172 if len(errs) > 0 { 173 t.Errorf("unexpected parse errors:") 174 for _, err := range errs { 175 t.Errorf(" %s", err) 176 } 177 t.FailNow() 178 } 179 180 _, errs = ctx.ResolveDependencies(nil) 181 if len(errs) > 0 { 182 t.Errorf("unexpected dep errors:") 183 for _, err := range errs { 184 t.Errorf(" %s", err) 185 } 186 t.FailNow() 187 } 188 189 var outputDown string 190 var outputUp string 191 topModule := ctx.modulesFromName("A", nil)[0] 192 ctx.walkDeps(topModule, 193 func(dep depInfo, parent *moduleInfo) bool { 194 if dep.module.logicModule.(Walker).Walk() { 195 outputDown += ctx.ModuleName(dep.module.logicModule) 196 return true 197 } 198 return false 199 }, 200 func(dep depInfo, parent *moduleInfo) { 201 if dep.module.logicModule.(Walker).Walk() { 202 outputUp += ctx.ModuleName(dep.module.logicModule) 203 } 204 }) 205 if outputDown != "CFG" { 206 t.Fatalf("unexpected walkDeps behaviour: %s\ndown should be: CFG", outputDown) 207 } 208 if outputUp != "GFC" { 209 t.Fatalf("unexpected walkDeps behaviour: %s\nup should be: GFC", outputUp) 210 } 211 } 212 213 func TestCreateModule(t *testing.T) { 214 ctx := newContext() 215 ctx.MockFileSystem(map[string][]byte{ 216 "Blueprints": []byte(` 217 foo_module { 218 name: "A", 219 deps: ["B", "C"], 220 } 221 `), 222 }) 223 224 ctx.RegisterTopDownMutator("create", createTestMutator) 225 ctx.RegisterBottomUpMutator("deps", blueprintDepsMutator) 226 227 ctx.RegisterModuleType("foo_module", newFooModule) 228 ctx.RegisterModuleType("bar_module", newBarModule) 229 _, errs := ctx.ParseBlueprintsFiles("Blueprints") 230 if len(errs) > 0 { 231 t.Errorf("unexpected parse errors:") 232 for _, err := range errs { 233 t.Errorf(" %s", err) 234 } 235 t.FailNow() 236 } 237 238 _, errs = ctx.ResolveDependencies(nil) 239 if len(errs) > 0 { 240 t.Errorf("unexpected dep errors:") 241 for _, err := range errs { 242 t.Errorf(" %s", err) 243 } 244 t.FailNow() 245 } 246 247 a := ctx.modulesFromName("A", nil)[0].logicModule.(*fooModule) 248 b := ctx.modulesFromName("B", nil)[0].logicModule.(*barModule) 249 c := ctx.modulesFromName("C", nil)[0].logicModule.(*barModule) 250 d := ctx.modulesFromName("D", nil)[0].logicModule.(*fooModule) 251 252 checkDeps := func(m Module, expected string) { 253 var deps []string 254 ctx.VisitDirectDeps(m, func(m Module) { 255 deps = append(deps, ctx.ModuleName(m)) 256 }) 257 got := strings.Join(deps, ",") 258 if got != expected { 259 t.Errorf("unexpected %q dependencies, got %q expected %q", 260 ctx.ModuleName(m), got, expected) 261 } 262 } 263 264 checkDeps(a, "B,C") 265 checkDeps(b, "D") 266 checkDeps(c, "D") 267 checkDeps(d, "") 268 } 269 270 func createTestMutator(ctx TopDownMutatorContext) { 271 type props struct { 272 Name string 273 Deps []string 274 } 275 276 ctx.CreateModule(newBarModule, &props{ 277 Name: "B", 278 Deps: []string{"D"}, 279 }) 280 281 ctx.CreateModule(newBarModule, &props{ 282 Name: "C", 283 Deps: []string{"D"}, 284 }) 285 286 ctx.CreateModule(newFooModule, &props{ 287 Name: "D", 288 }) 289 } 290 291 func TestWalkFileOrder(t *testing.T) { 292 // Run the test once to see how long it normally takes 293 start := time.Now() 294 doTestWalkFileOrder(t, time.Duration(0)) 295 duration := time.Since(start) 296 297 // Run the test again, but put enough of a sleep into each visitor to detect ordering 298 // problems if they exist 299 doTestWalkFileOrder(t, duration) 300 } 301 302 // test that WalkBlueprintsFiles calls asyncVisitor in the right order 303 func doTestWalkFileOrder(t *testing.T, sleepDuration time.Duration) { 304 // setup mock context 305 ctx := newContext() 306 mockFiles := map[string][]byte{ 307 "Blueprints": []byte(` 308 sample_module { 309 name: "a", 310 } 311 `), 312 "dir1/Blueprints": []byte(` 313 sample_module { 314 name: "b", 315 } 316 `), 317 "dir1/dir2/Blueprints": []byte(` 318 sample_module { 319 name: "c", 320 } 321 `), 322 } 323 ctx.MockFileSystem(mockFiles) 324 325 // prepare to monitor the visit order 326 visitOrder := []string{} 327 visitLock := sync.Mutex{} 328 correctVisitOrder := []string{"Blueprints", "dir1/Blueprints", "dir1/dir2/Blueprints"} 329 330 // sleep longer when processing the earlier files 331 chooseSleepDuration := func(fileName string) (duration time.Duration) { 332 duration = time.Duration(0) 333 for i := len(correctVisitOrder) - 1; i >= 0; i-- { 334 if fileName == correctVisitOrder[i] { 335 return duration 336 } 337 duration = duration + sleepDuration 338 } 339 panic("unrecognized file name " + fileName) 340 } 341 342 visitor := func(file *parser.File) { 343 time.Sleep(chooseSleepDuration(file.Name)) 344 visitLock.Lock() 345 defer visitLock.Unlock() 346 visitOrder = append(visitOrder, file.Name) 347 } 348 keys := []string{"Blueprints", "dir1/Blueprints", "dir1/dir2/Blueprints"} 349 350 // visit the blueprints files 351 ctx.WalkBlueprintsFiles(".", keys, visitor) 352 353 // check the order 354 if !reflect.DeepEqual(visitOrder, correctVisitOrder) { 355 t.Errorf("Incorrect visit order; expected %v, got %v", correctVisitOrder, visitOrder) 356 } 357 } 358 359 // test that WalkBlueprintsFiles reports syntax errors 360 func TestWalkingWithSyntaxError(t *testing.T) { 361 // setup mock context 362 ctx := newContext() 363 mockFiles := map[string][]byte{ 364 "Blueprints": []byte(` 365 sample_module { 366 name: "a" "b", 367 } 368 `), 369 "dir1/Blueprints": []byte(` 370 sample_module { 371 name: "b", 372 `), 373 "dir1/dir2/Blueprints": []byte(` 374 sample_module { 375 name: "c", 376 } 377 `), 378 } 379 ctx.MockFileSystem(mockFiles) 380 381 keys := []string{"Blueprints", "dir1/Blueprints", "dir1/dir2/Blueprints"} 382 383 // visit the blueprints files 384 _, errs := ctx.WalkBlueprintsFiles(".", keys, func(file *parser.File) {}) 385 386 expectedErrs := []error{ 387 errors.New(`Blueprints:3:18: expected "}", found String`), 388 errors.New(`dir1/Blueprints:4:3: expected "}", found EOF`), 389 } 390 if fmt.Sprintf("%s", expectedErrs) != fmt.Sprintf("%s", errs) { 391 t.Errorf("Incorrect errors; expected:\n%s\ngot:\n%s", expectedErrs, errs) 392 } 393 394 }