blob: 635f73e50837522ad7ef24191ac0e14af316ca62 [file] [log] [blame]
Colin Cross8e0c5112015-01-23 14:15:10 -08001// 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
Jamie Gennis1bc967e2014-05-27 16:34:41 -070015package blueprint
16
17import (
18 "bytes"
Jeff Gastona7e408a2017-12-05 15:11:55 -080019 "errors"
20 "fmt"
Jeff Gaston656870f2017-11-29 18:37:31 -080021 "reflect"
Colin Crossaf4fd212017-07-28 14:32:36 -070022 "strings"
Jeff Gaston656870f2017-11-29 18:37:31 -080023 "sync"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070024 "testing"
Jeff Gaston656870f2017-11-29 18:37:31 -080025 "time"
Jeff Gastonc3e28442017-08-09 15:13:12 -070026
27 "github.com/google/blueprint/parser"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070028)
29
Yuchen Wuf9958462015-10-09 17:31:27 -070030type Walker interface {
31 Walk() bool
32}
33
Jamie Gennis1bc967e2014-05-27 16:34:41 -070034type fooModule struct {
Colin Cross0b7e83e2016-05-17 14:58:05 -070035 SimpleName
Jamie Gennis1bc967e2014-05-27 16:34:41 -070036 properties struct {
Colin Cross0b7e83e2016-05-17 14:58:05 -070037 Deps []string
38 Foo string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070039 }
40}
41
Jamie Gennis68540da2014-10-06 09:10:40 -070042func newFooModule() (Module, []interface{}) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -070043 m := &fooModule{}
Colin Cross0b7e83e2016-05-17 14:58:05 -070044 return m, []interface{}{&m.properties, &m.SimpleName.Properties}
Jamie Gennis1bc967e2014-05-27 16:34:41 -070045}
46
47func (f *fooModule) GenerateBuildActions(ModuleContext) {
48}
49
Colin Cross0b7e83e2016-05-17 14:58:05 -070050func (f *fooModule) DynamicDependencies(ctx DynamicDependerModuleContext) []string {
51 return f.properties.Deps
52}
53
Jamie Gennis1bc967e2014-05-27 16:34:41 -070054func (f *fooModule) Foo() string {
55 return f.properties.Foo
56}
57
Yuchen Wuf9958462015-10-09 17:31:27 -070058func (f *fooModule) Walk() bool {
59 return true
60}
61
Jamie Gennis1bc967e2014-05-27 16:34:41 -070062type barModule struct {
Colin Cross0b7e83e2016-05-17 14:58:05 -070063 SimpleName
Jamie Gennis1bc967e2014-05-27 16:34:41 -070064 properties struct {
Colin Cross0b7e83e2016-05-17 14:58:05 -070065 Deps []string
66 Bar bool
Jamie Gennis1bc967e2014-05-27 16:34:41 -070067 }
68}
69
Jamie Gennis68540da2014-10-06 09:10:40 -070070func newBarModule() (Module, []interface{}) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -070071 m := &barModule{}
Colin Cross0b7e83e2016-05-17 14:58:05 -070072 return m, []interface{}{&m.properties, &m.SimpleName.Properties}
73}
74
75func (b *barModule) DynamicDependencies(ctx DynamicDependerModuleContext) []string {
76 return b.properties.Deps
Jamie Gennis1bc967e2014-05-27 16:34:41 -070077}
78
79func (b *barModule) GenerateBuildActions(ModuleContext) {
80}
81
82func (b *barModule) Bar() bool {
83 return b.properties.Bar
84}
85
Yuchen Wuf9958462015-10-09 17:31:27 -070086func (b *barModule) Walk() bool {
87 return false
88}
89
Jamie Gennis1bc967e2014-05-27 16:34:41 -070090func TestContextParse(t *testing.T) {
91 ctx := NewContext()
Jamie Gennis68540da2014-10-06 09:10:40 -070092 ctx.RegisterModuleType("foo_module", newFooModule)
93 ctx.RegisterModuleType("bar_module", newBarModule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -070094
95 r := bytes.NewBufferString(`
96 foo_module {
Colin Cross0b7e83e2016-05-17 14:58:05 -070097 name: "MyFooModule",
Jamie Gennis1bc967e2014-05-27 16:34:41 -070098 deps: ["MyBarModule"],
99 }
100
101 bar_module {
Colin Cross0b7e83e2016-05-17 14:58:05 -0700102 name: "MyBarModule",
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700103 }
104 `)
105
Jeff Gaston656870f2017-11-29 18:37:31 -0800106 _, _, errs := ctx.parseOne(".", "Blueprint", r, parser.NewScope(nil), nil)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700107 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
Colin Cross874a3462017-07-31 17:26:06 -0700115 _, errs = ctx.ResolveDependencies(nil)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700116 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 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700123}
Yuchen Wuf9958462015-10-09 17:31:27 -0700124
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.
130func TestWalkDeps(t *testing.T) {
131 ctx := NewContext()
Colin Crossd7b0f602016-06-02 15:30:20 -0700132 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
Yuchen Wuf9958462015-10-09 17:31:27 -0700169 ctx.RegisterModuleType("foo_module", newFooModule)
170 ctx.RegisterModuleType("bar_module", newBarModule)
Colin Crossd7b0f602016-06-02 15:30:20 -0700171 _, 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
Colin Cross874a3462017-07-31 17:26:06 -0700180 _, errs = ctx.ResolveDependencies(nil)
Colin Crossd7b0f602016-06-02 15:30:20 -0700181 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 }
Yuchen Wuf9958462015-10-09 17:31:27 -0700188
Colin Crossbafd5f52016-08-06 22:52:01 -0700189 var outputDown string
190 var outputUp string
Jeff Gastond70bf752017-11-10 15:12:08 -0800191 topModule := ctx.modulesFromName("A", nil)[0]
Yuchen Wuf9958462015-10-09 17:31:27 -0700192 ctx.walkDeps(topModule,
Colin Crossbafd5f52016-08-06 22:52:01 -0700193 func(dep depInfo, parent *moduleInfo) bool {
194 if dep.module.logicModule.(Walker).Walk() {
195 outputDown += ctx.ModuleName(dep.module.logicModule)
Yuchen Wuf9958462015-10-09 17:31:27 -0700196 return true
197 }
198 return false
Colin Crossbafd5f52016-08-06 22:52:01 -0700199 },
200 func(dep depInfo, parent *moduleInfo) {
201 if dep.module.logicModule.(Walker).Walk() {
202 outputUp += ctx.ModuleName(dep.module.logicModule)
203 }
Yuchen Wuf9958462015-10-09 17:31:27 -0700204 })
Colin Crossbafd5f52016-08-06 22:52:01 -0700205 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)
Yuchen Wuf9958462015-10-09 17:31:27 -0700210 }
211}
Colin Crossaf4fd212017-07-28 14:32:36 -0700212
213func 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
Colin Cross874a3462017-07-31 17:26:06 -0700238 _, errs = ctx.ResolveDependencies(nil)
Colin Crossaf4fd212017-07-28 14:32:36 -0700239 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
Jeff Gastond70bf752017-11-10 15:12:08 -0800247 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)
Colin Crossaf4fd212017-07-28 14:32:36 -0700251
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
270func 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}
Jeff Gaston656870f2017-11-29 18:37:31 -0800290
291func 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
303func 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}
Jeff Gastona7e408a2017-12-05 15:11:55 -0800358
359// test that WalkBlueprintsFiles reports syntax errors
360func 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}