blob: c0ce80371bb3b153cbc5bdcbc19a099f1edcd8fc [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 Gaston656870f2017-11-29 18:37:31 -080019 "reflect"
Colin Crossaf4fd212017-07-28 14:32:36 -070020 "strings"
Jeff Gaston656870f2017-11-29 18:37:31 -080021 "sync"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070022 "testing"
Jeff Gaston656870f2017-11-29 18:37:31 -080023 "time"
Jeff Gastonc3e28442017-08-09 15:13:12 -070024
25 "github.com/google/blueprint/parser"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070026)
27
Yuchen Wuf9958462015-10-09 17:31:27 -070028type Walker interface {
29 Walk() bool
30}
31
Jamie Gennis1bc967e2014-05-27 16:34:41 -070032type fooModule struct {
Colin Cross0b7e83e2016-05-17 14:58:05 -070033 SimpleName
Jamie Gennis1bc967e2014-05-27 16:34:41 -070034 properties struct {
Colin Cross0b7e83e2016-05-17 14:58:05 -070035 Deps []string
36 Foo string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070037 }
38}
39
Jamie Gennis68540da2014-10-06 09:10:40 -070040func newFooModule() (Module, []interface{}) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -070041 m := &fooModule{}
Colin Cross0b7e83e2016-05-17 14:58:05 -070042 return m, []interface{}{&m.properties, &m.SimpleName.Properties}
Jamie Gennis1bc967e2014-05-27 16:34:41 -070043}
44
45func (f *fooModule) GenerateBuildActions(ModuleContext) {
46}
47
Colin Cross0b7e83e2016-05-17 14:58:05 -070048func (f *fooModule) DynamicDependencies(ctx DynamicDependerModuleContext) []string {
49 return f.properties.Deps
50}
51
Jamie Gennis1bc967e2014-05-27 16:34:41 -070052func (f *fooModule) Foo() string {
53 return f.properties.Foo
54}
55
Yuchen Wuf9958462015-10-09 17:31:27 -070056func (f *fooModule) Walk() bool {
57 return true
58}
59
Jamie Gennis1bc967e2014-05-27 16:34:41 -070060type barModule struct {
Colin Cross0b7e83e2016-05-17 14:58:05 -070061 SimpleName
Jamie Gennis1bc967e2014-05-27 16:34:41 -070062 properties struct {
Colin Cross0b7e83e2016-05-17 14:58:05 -070063 Deps []string
64 Bar bool
Jamie Gennis1bc967e2014-05-27 16:34:41 -070065 }
66}
67
Jamie Gennis68540da2014-10-06 09:10:40 -070068func newBarModule() (Module, []interface{}) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -070069 m := &barModule{}
Colin Cross0b7e83e2016-05-17 14:58:05 -070070 return m, []interface{}{&m.properties, &m.SimpleName.Properties}
71}
72
73func (b *barModule) DynamicDependencies(ctx DynamicDependerModuleContext) []string {
74 return b.properties.Deps
Jamie Gennis1bc967e2014-05-27 16:34:41 -070075}
76
77func (b *barModule) GenerateBuildActions(ModuleContext) {
78}
79
80func (b *barModule) Bar() bool {
81 return b.properties.Bar
82}
83
Yuchen Wuf9958462015-10-09 17:31:27 -070084func (b *barModule) Walk() bool {
85 return false
86}
87
Jamie Gennis1bc967e2014-05-27 16:34:41 -070088func TestContextParse(t *testing.T) {
89 ctx := NewContext()
Jamie Gennis68540da2014-10-06 09:10:40 -070090 ctx.RegisterModuleType("foo_module", newFooModule)
91 ctx.RegisterModuleType("bar_module", newBarModule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -070092
93 r := bytes.NewBufferString(`
94 foo_module {
Colin Cross0b7e83e2016-05-17 14:58:05 -070095 name: "MyFooModule",
Jamie Gennis1bc967e2014-05-27 16:34:41 -070096 deps: ["MyBarModule"],
97 }
98
99 bar_module {
Colin Cross0b7e83e2016-05-17 14:58:05 -0700100 name: "MyBarModule",
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700101 }
102 `)
103
Jeff Gaston656870f2017-11-29 18:37:31 -0800104 _, _, errs := ctx.parseOne(".", "Blueprint", r, parser.NewScope(nil), nil)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700105 if len(errs) > 0 {
106 t.Errorf("unexpected parse errors:")
107 for _, err := range errs {
108 t.Errorf(" %s", err)
109 }
110 t.FailNow()
111 }
112
Colin Cross874a3462017-07-31 17:26:06 -0700113 _, errs = ctx.ResolveDependencies(nil)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700114 if len(errs) > 0 {
115 t.Errorf("unexpected dep errors:")
116 for _, err := range errs {
117 t.Errorf(" %s", err)
118 }
119 t.FailNow()
120 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700121}
Yuchen Wuf9958462015-10-09 17:31:27 -0700122
123// |---B===D - represents a non-walkable edge
124// A = represents a walkable edge
125// |===C---E===G
126// | | A should not be visited because it's the root node.
127// |===F===| B, D and E should not be walked.
128func TestWalkDeps(t *testing.T) {
129 ctx := NewContext()
Colin Crossd7b0f602016-06-02 15:30:20 -0700130 ctx.MockFileSystem(map[string][]byte{
131 "Blueprints": []byte(`
132 foo_module {
133 name: "A",
134 deps: ["B", "C"],
135 }
136
137 bar_module {
138 name: "B",
139 deps: ["D"],
140 }
141
142 foo_module {
143 name: "C",
144 deps: ["E", "F"],
145 }
146
147 foo_module {
148 name: "D",
149 }
150
151 bar_module {
152 name: "E",
153 deps: ["G"],
154 }
155
156 foo_module {
157 name: "F",
158 deps: ["G"],
159 }
160
161 foo_module {
162 name: "G",
163 }
164 `),
165 })
166
Yuchen Wuf9958462015-10-09 17:31:27 -0700167 ctx.RegisterModuleType("foo_module", newFooModule)
168 ctx.RegisterModuleType("bar_module", newBarModule)
Colin Crossd7b0f602016-06-02 15:30:20 -0700169 _, errs := ctx.ParseBlueprintsFiles("Blueprints")
170 if len(errs) > 0 {
171 t.Errorf("unexpected parse errors:")
172 for _, err := range errs {
173 t.Errorf(" %s", err)
174 }
175 t.FailNow()
176 }
177
Colin Cross874a3462017-07-31 17:26:06 -0700178 _, errs = ctx.ResolveDependencies(nil)
Colin Crossd7b0f602016-06-02 15:30:20 -0700179 if len(errs) > 0 {
180 t.Errorf("unexpected dep errors:")
181 for _, err := range errs {
182 t.Errorf(" %s", err)
183 }
184 t.FailNow()
185 }
Yuchen Wuf9958462015-10-09 17:31:27 -0700186
Colin Crossbafd5f52016-08-06 22:52:01 -0700187 var outputDown string
188 var outputUp string
Jeff Gastond70bf752017-11-10 15:12:08 -0800189 topModule := ctx.modulesFromName("A", nil)[0]
Yuchen Wuf9958462015-10-09 17:31:27 -0700190 ctx.walkDeps(topModule,
Colin Crossbafd5f52016-08-06 22:52:01 -0700191 func(dep depInfo, parent *moduleInfo) bool {
192 if dep.module.logicModule.(Walker).Walk() {
193 outputDown += ctx.ModuleName(dep.module.logicModule)
Yuchen Wuf9958462015-10-09 17:31:27 -0700194 return true
195 }
196 return false
Colin Crossbafd5f52016-08-06 22:52:01 -0700197 },
198 func(dep depInfo, parent *moduleInfo) {
199 if dep.module.logicModule.(Walker).Walk() {
200 outputUp += ctx.ModuleName(dep.module.logicModule)
201 }
Yuchen Wuf9958462015-10-09 17:31:27 -0700202 })
Colin Crossbafd5f52016-08-06 22:52:01 -0700203 if outputDown != "CFG" {
204 t.Fatalf("unexpected walkDeps behaviour: %s\ndown should be: CFG", outputDown)
205 }
206 if outputUp != "GFC" {
207 t.Fatalf("unexpected walkDeps behaviour: %s\nup should be: GFC", outputUp)
Yuchen Wuf9958462015-10-09 17:31:27 -0700208 }
209}
Colin Crossaf4fd212017-07-28 14:32:36 -0700210
211func TestCreateModule(t *testing.T) {
212 ctx := newContext()
213 ctx.MockFileSystem(map[string][]byte{
214 "Blueprints": []byte(`
215 foo_module {
216 name: "A",
217 deps: ["B", "C"],
218 }
219 `),
220 })
221
222 ctx.RegisterTopDownMutator("create", createTestMutator)
223 ctx.RegisterBottomUpMutator("deps", blueprintDepsMutator)
224
225 ctx.RegisterModuleType("foo_module", newFooModule)
226 ctx.RegisterModuleType("bar_module", newBarModule)
227 _, errs := ctx.ParseBlueprintsFiles("Blueprints")
228 if len(errs) > 0 {
229 t.Errorf("unexpected parse errors:")
230 for _, err := range errs {
231 t.Errorf(" %s", err)
232 }
233 t.FailNow()
234 }
235
Colin Cross874a3462017-07-31 17:26:06 -0700236 _, errs = ctx.ResolveDependencies(nil)
Colin Crossaf4fd212017-07-28 14:32:36 -0700237 if len(errs) > 0 {
238 t.Errorf("unexpected dep errors:")
239 for _, err := range errs {
240 t.Errorf(" %s", err)
241 }
242 t.FailNow()
243 }
244
Jeff Gastond70bf752017-11-10 15:12:08 -0800245 a := ctx.modulesFromName("A", nil)[0].logicModule.(*fooModule)
246 b := ctx.modulesFromName("B", nil)[0].logicModule.(*barModule)
247 c := ctx.modulesFromName("C", nil)[0].logicModule.(*barModule)
248 d := ctx.modulesFromName("D", nil)[0].logicModule.(*fooModule)
Colin Crossaf4fd212017-07-28 14:32:36 -0700249
250 checkDeps := func(m Module, expected string) {
251 var deps []string
252 ctx.VisitDirectDeps(m, func(m Module) {
253 deps = append(deps, ctx.ModuleName(m))
254 })
255 got := strings.Join(deps, ",")
256 if got != expected {
257 t.Errorf("unexpected %q dependencies, got %q expected %q",
258 ctx.ModuleName(m), got, expected)
259 }
260 }
261
262 checkDeps(a, "B,C")
263 checkDeps(b, "D")
264 checkDeps(c, "D")
265 checkDeps(d, "")
266}
267
268func createTestMutator(ctx TopDownMutatorContext) {
269 type props struct {
270 Name string
271 Deps []string
272 }
273
274 ctx.CreateModule(newBarModule, &props{
275 Name: "B",
276 Deps: []string{"D"},
277 })
278
279 ctx.CreateModule(newBarModule, &props{
280 Name: "C",
281 Deps: []string{"D"},
282 })
283
284 ctx.CreateModule(newFooModule, &props{
285 Name: "D",
286 })
287}
Jeff Gaston656870f2017-11-29 18:37:31 -0800288
289func TestWalkFileOrder(t *testing.T) {
290 // Run the test once to see how long it normally takes
291 start := time.Now()
292 doTestWalkFileOrder(t, time.Duration(0))
293 duration := time.Since(start)
294
295 // Run the test again, but put enough of a sleep into each visitor to detect ordering
296 // problems if they exist
297 doTestWalkFileOrder(t, duration)
298}
299
300// test that WalkBlueprintsFiles calls asyncVisitor in the right order
301func doTestWalkFileOrder(t *testing.T, sleepDuration time.Duration) {
302 // setup mock context
303 ctx := newContext()
304 mockFiles := map[string][]byte{
305 "Blueprints": []byte(`
306 sample_module {
307 name: "a",
308 }
309 `),
310 "dir1/Blueprints": []byte(`
311 sample_module {
312 name: "b",
313 }
314 `),
315 "dir1/dir2/Blueprints": []byte(`
316 sample_module {
317 name: "c",
318 }
319 `),
320 }
321 ctx.MockFileSystem(mockFiles)
322
323 // prepare to monitor the visit order
324 visitOrder := []string{}
325 visitLock := sync.Mutex{}
326 correctVisitOrder := []string{"Blueprints", "dir1/Blueprints", "dir1/dir2/Blueprints"}
327
328 // sleep longer when processing the earlier files
329 chooseSleepDuration := func(fileName string) (duration time.Duration) {
330 duration = time.Duration(0)
331 for i := len(correctVisitOrder) - 1; i >= 0; i-- {
332 if fileName == correctVisitOrder[i] {
333 return duration
334 }
335 duration = duration + sleepDuration
336 }
337 panic("unrecognized file name " + fileName)
338 }
339
340 visitor := func(file *parser.File) {
341 time.Sleep(chooseSleepDuration(file.Name))
342 visitLock.Lock()
343 defer visitLock.Unlock()
344 visitOrder = append(visitOrder, file.Name)
345 }
346 keys := []string{"Blueprints", "dir1/Blueprints", "dir1/dir2/Blueprints"}
347
348 // visit the blueprints files
349 ctx.WalkBlueprintsFiles(".", keys, visitor)
350
351 // check the order
352 if !reflect.DeepEqual(visitOrder, correctVisitOrder) {
353 t.Errorf("Incorrect visit order; expected %v, got %v", correctVisitOrder, visitOrder)
354 }
355}