Merge pull request #111 from colincross/visit

Reimplement VisitDirectDeps[If] on Context.walkdeps
This commit is contained in:
colincross 2016-08-09 13:37:18 -07:00 committed by GitHub
commit 1c1ec45ebb
8 changed files with 444 additions and 150 deletions

View file

@ -8,6 +8,7 @@ bootstrap_go_package(
pkgPath = "github.com/google/blueprint",
srcs = [
"context.go",
"fs.go",
"live_tracker.go",
"mangle.go",
"module_ctx.go",
@ -25,6 +26,7 @@ bootstrap_go_package(
"ninja_writer_test.go",
"splice_modules_test.go",
"unpack_test.go",
"visit_test.go",
],
)

View file

@ -60,8 +60,8 @@ rule g.bootstrap.link
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
: g.bootstrap.compile ${g.bootstrap.srcDir}/context.go $
${g.bootstrap.srcDir}/live_tracker.go ${g.bootstrap.srcDir}/mangle.go $
${g.bootstrap.srcDir}/module_ctx.go $
${g.bootstrap.srcDir}/fs.go ${g.bootstrap.srcDir}/live_tracker.go $
${g.bootstrap.srcDir}/mangle.go ${g.bootstrap.srcDir}/module_ctx.go $
${g.bootstrap.srcDir}/ninja_defs.go $
${g.bootstrap.srcDir}/ninja_strings.go $
${g.bootstrap.srcDir}/ninja_writer.go $
@ -81,7 +81,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: Blueprints:81:1
# Defined: Blueprints:83:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
@ -108,7 +108,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: Blueprints:100:1
# Defined: Blueprints:102:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
@ -128,7 +128,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: Blueprints:47:1
# Defined: Blueprints:49:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
@ -143,7 +143,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: Blueprints:31:1
# Defined: Blueprints:33:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
@ -161,7 +161,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: Blueprints:53:1
# Defined: Blueprints:55:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
@ -176,7 +176,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: Blueprints:65:1
# Defined: Blueprints:67:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
@ -194,7 +194,7 @@ default $
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: Blueprints:143:1
# Defined: Blueprints:145:1
build ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/choosestage.a: $
g.bootstrap.compile ${g.bootstrap.srcDir}/choosestage/choosestage.go | $
@ -217,7 +217,7 @@ default ${g.bootstrap.BinDir}/choosestage
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: Blueprints:133:1
# Defined: Blueprints:135:1
build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a: $
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestmain/gotestmain.go | $
@ -240,7 +240,7 @@ default ${g.bootstrap.BinDir}/gotestmain
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: Blueprints:138:1
# Defined: Blueprints:140:1
build ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a: $
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestrunner/gotestrunner.go $
@ -263,7 +263,7 @@ default ${g.bootstrap.BinDir}/gotestrunner
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: Blueprints:112:1
# Defined: Blueprints:114:1
build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a: $
g.bootstrap.compile ${g.bootstrap.srcDir}/bootstrap/minibp/main.go | $

View file

@ -19,7 +19,6 @@ import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"reflect"
"runtime"
@ -98,6 +97,8 @@ type Context struct {
// set lazily by sortedModuleNames
cachedSortedModuleNames []string
fs fileSystem
}
// An Error describes a problem that was encountered that is related to a
@ -246,6 +247,7 @@ func NewContext() *Context {
moduleGroups: make(map[string]*moduleGroup),
moduleInfo: make(map[Module]*moduleInfo),
moduleNinjaNames: make(map[string]*moduleGroup),
fs: fs,
}
ctx.RegisterBottomUpMutator("blueprint_deps", blueprintDepsMutator)
@ -711,6 +713,14 @@ loop:
return
}
// MockFileSystem causes the Context to replace all reads with accesses to the provided map of
// filenames to contents stored as a byte slice.
func (c *Context) MockFileSystem(files map[string][]byte) {
c.fs = &mockFS{
files: files,
}
}
// parseBlueprintFile parses a single Blueprints file, returning any errors through
// errsCh, any defined modules through modulesCh, any sub-Blueprints files through
// blueprintsCh, and any dependencies on Blueprints files or directories through
@ -719,7 +729,7 @@ func (c *Context) parseBlueprintsFile(filename string, scope *parser.Scope, root
errsCh chan<- []error, fileCh chan<- *parser.File, blueprintsCh chan<- stringAndScope,
depsCh chan<- string) {
f, err := os.Open(filename)
f, err := c.fs.Open(filename)
if err != nil {
errsCh <- []error{err}
return
@ -772,15 +782,15 @@ func (c *Context) findBuildBlueprints(dir string, build []string,
deps = append(deps, matchedDirs...)
for _, foundBlueprints := range matches {
fileInfo, err := os.Stat(foundBlueprints)
if os.IsNotExist(err) {
exists, dir, err := c.fs.Exists(foundBlueprints)
if err != nil {
errs = append(errs, err)
} else if !exists {
errs = append(errs, &Error{
Err: fmt.Errorf("%q not found", foundBlueprints),
})
continue
}
if fileInfo.IsDir() {
} else if dir {
errs = append(errs, &Error{
Err: fmt.Errorf("%q is a directory", foundBlueprints),
})
@ -819,29 +829,34 @@ func (c *Context) findSubdirBlueprints(dir string, subdirs []string, subdirsPos
deps = append(deps, matchedDirs...)
for _, foundSubdir := range matches {
fileInfo, subdirStatErr := os.Stat(foundSubdir)
exists, dir, subdirStatErr := c.fs.Exists(foundSubdir)
if subdirStatErr != nil {
errs = append(errs, subdirStatErr)
continue
}
// Skip files
if !fileInfo.IsDir() {
if !dir {
continue
}
var subBlueprints string
if subBlueprintsName != "" {
subBlueprints = filepath.Join(foundSubdir, subBlueprintsName)
_, err = os.Stat(subBlueprints)
exists, _, err = c.fs.Exists(subBlueprints)
}
if os.IsNotExist(err) || subBlueprints == "" {
if err == nil && (!exists || subBlueprints == "") {
subBlueprints = filepath.Join(foundSubdir, "Blueprints")
_, err = os.Stat(subBlueprints)
exists, _, err = c.fs.Exists(subBlueprints)
}
if os.IsNotExist(err) {
if err != nil {
errs = append(errs, err)
continue
}
if !exists {
// There is no Blueprints file in this subdirectory. We
// need to add the directory to the list of dependencies
// so that if someone adds a Blueprints file in the
@ -1978,15 +1993,15 @@ func (c *Context) processLocalBuildActions(out, in *localBuildActions,
}
func (c *Context) walkDeps(topModule *moduleInfo,
visit func(Module, Module) bool) {
visitDown func(depInfo, *moduleInfo) bool, visitUp func(depInfo, *moduleInfo)) {
visited := make(map[*moduleInfo]bool)
var visiting *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "WalkDeps(%s, %s) for dependency %s",
topModule, funcName(visit), visiting))
panic(newPanicErrorf(r, "WalkDeps(%s, %s, %s) for dependency %s",
topModule, funcName(visitDown), funcName(visitUp), visiting))
}
}()
@ -1996,9 +2011,16 @@ func (c *Context) walkDeps(topModule *moduleInfo,
if !visited[dep.module] {
visited[dep.module] = true
visiting = dep.module
if visit(dep.module.logicModule, module.logicModule) {
recurse := true
if visitDown != nil {
recurse = visitDown(dep, module)
}
if recurse {
walk(dep.module)
}
if visitUp != nil {
visitUp(dep, module)
}
}
}
}
@ -2008,68 +2030,6 @@ func (c *Context) walkDeps(topModule *moduleInfo,
type innerPanicError error
func (c *Context) visitDepsDepthFirst(topModule *moduleInfo, visit func(Module)) {
visited := make(map[*moduleInfo]bool)
var visiting *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s",
topModule, funcName(visit), visiting))
}
}()
var walk func(module *moduleInfo)
walk = func(module *moduleInfo) {
visited[module] = true
for _, dep := range module.directDeps {
if !visited[dep.module] {
walk(dep.module)
}
}
if module != topModule {
visiting = module
visit(module.logicModule)
}
}
walk(topModule)
}
func (c *Context) visitDepsDepthFirstIf(topModule *moduleInfo, pred func(Module) bool,
visit func(Module)) {
visited := make(map[*moduleInfo]bool)
var visiting *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s",
topModule, funcName(pred), funcName(visit), visiting))
}
}()
var walk func(module *moduleInfo)
walk = func(module *moduleInfo) {
visited[module] = true
for _, dep := range module.directDeps {
if !visited[dep.module] {
walk(dep.module)
}
}
if module != topModule {
if pred(module.logicModule) {
visiting = module
visit(module.logicModule)
}
}
}
walk(topModule)
}
func (c *Context) sortedModuleNames() []string {
if c.cachedSortedModuleNames == nil {
c.cachedSortedModuleNames = make([]string, 0, len(c.moduleGroups))
@ -2399,13 +2359,43 @@ func (c *Context) VisitAllModulesIf(pred func(Module) bool,
func (c *Context) VisitDepsDepthFirst(module Module,
visit func(Module)) {
c.visitDepsDepthFirst(c.moduleInfo[module], visit)
topModule := c.moduleInfo[module]
var visiting *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s",
topModule, funcName(visit), visiting))
}
}()
c.walkDeps(topModule, nil, func(dep depInfo, parent *moduleInfo) {
visiting = dep.module
visit(dep.module.logicModule)
})
}
func (c *Context) VisitDepsDepthFirstIf(module Module,
pred func(Module) bool, visit func(Module)) {
c.visitDepsDepthFirstIf(c.moduleInfo[module], pred, visit)
topModule := c.moduleInfo[module]
var visiting *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s",
topModule, funcName(pred), funcName(visit), visiting))
}
}()
c.walkDeps(topModule, nil, func(dep depInfo, parent *moduleInfo) {
if pred(dep.module.logicModule) {
visiting = dep.module
visit(dep.module.logicModule)
}
})
}
func (c *Context) PrimaryModule(module Module) Module {

View file

@ -109,22 +109,83 @@ func TestContextParse(t *testing.T) {
// |===F===| B, D and E should not be walked.
func TestWalkDeps(t *testing.T) {
ctx := NewContext()
ctx.MockFileSystem(map[string][]byte{
"Blueprints": []byte(`
foo_module {
name: "A",
deps: ["B", "C"],
}
bar_module {
name: "B",
deps: ["D"],
}
foo_module {
name: "C",
deps: ["E", "F"],
}
foo_module {
name: "D",
}
bar_module {
name: "E",
deps: ["G"],
}
foo_module {
name: "F",
deps: ["G"],
}
foo_module {
name: "G",
}
`),
})
ctx.RegisterModuleType("foo_module", newFooModule)
ctx.RegisterModuleType("bar_module", newBarModule)
ctx.ParseBlueprintsFiles("context_test_Blueprints")
ctx.ResolveDependencies(nil)
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
if len(errs) > 0 {
t.Errorf("unexpected parse errors:")
for _, err := range errs {
t.Errorf(" %s", err)
}
t.FailNow()
}
var output string
errs = ctx.ResolveDependencies(nil)
if len(errs) > 0 {
t.Errorf("unexpected dep errors:")
for _, err := range errs {
t.Errorf(" %s", err)
}
t.FailNow()
}
var outputDown string
var outputUp string
topModule := ctx.moduleGroups["A"].modules[0]
ctx.walkDeps(topModule,
func(module, parent Module) bool {
if module.(Walker).Walk() {
output += ctx.ModuleName(module)
func(dep depInfo, parent *moduleInfo) bool {
if dep.module.logicModule.(Walker).Walk() {
outputDown += ctx.ModuleName(dep.module.logicModule)
return true
}
return false
},
func(dep depInfo, parent *moduleInfo) {
if dep.module.logicModule.(Walker).Walk() {
outputUp += ctx.ModuleName(dep.module.logicModule)
}
})
if output != "CFG" {
t.Fatalf("unexpected walkDeps behaviour: %s\nshould be: CFG", output)
if outputDown != "CFG" {
t.Fatalf("unexpected walkDeps behaviour: %s\ndown should be: CFG", outputDown)
}
if outputUp != "GFC" {
t.Fatalf("unexpected walkDeps behaviour: %s\nup should be: GFC", outputUp)
}
}

View file

@ -1,32 +0,0 @@
foo_module {
name: "A",
deps: ["B", "C"],
}
bar_module {
name: "B",
deps: ["D"],
}
foo_module {
name: "C",
deps: ["E", "F"],
}
foo_module {
name: "D",
}
bar_module {
name: "E",
deps: ["G"],
}
foo_module {
name: "F",
deps: ["G"],
}
foo_module {
name: "G",
}

73
fs.go Normal file
View file

@ -0,0 +1,73 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package blueprint
import (
"bytes"
"io"
"io/ioutil"
"os"
)
// Based on Andrew Gerrand's "10 things you (probably) dont' know about Go"
var fs fileSystem = osFS{}
type fileSystem interface {
Open(name string) (io.ReadCloser, error)
Exists(name string) (bool, bool, error)
}
// osFS implements fileSystem using the local disk.
type osFS struct{}
func (osFS) Open(name string) (io.ReadCloser, error) { return os.Open(name) }
func (osFS) Exists(name string) (bool, bool, error) {
stat, err := os.Stat(name)
if err == nil {
return true, stat.IsDir(), nil
} else if os.IsNotExist(err) {
return false, false, nil
} else {
return false, false, err
}
}
type mockFS struct {
files map[string][]byte
}
func (m mockFS) Open(name string) (io.ReadCloser, error) {
if f, ok := m.files[name]; ok {
return struct {
io.Closer
*bytes.Reader
}{
ioutil.NopCloser(nil),
bytes.NewReader(f),
}, nil
}
return nil, &os.PathError{
Op: "open",
Path: name,
Err: os.ErrNotExist,
}
}
func (m mockFS) Exists(name string) (bool, bool, error) {
_, ok := m.files[name]
return ok, false, nil
}

View file

@ -155,11 +155,12 @@ type ModuleContext interface {
var _ BaseModuleContext = (*baseModuleContext)(nil)
type baseModuleContext struct {
context *Context
config interface{}
module *moduleInfo
errs []error
visiting depInfo
context *Context
config interface{}
module *moduleInfo
errs []error
visitingParent *moduleInfo
visitingDep depInfo
}
func (d *baseModuleContext) moduleInfo() *moduleInfo {
@ -255,11 +256,11 @@ func (m *baseModuleContext) OtherModuleErrorf(logicModule Module, format string,
func (m *baseModuleContext) OtherModuleDependencyTag(logicModule Module) DependencyTag {
// fast path for calling OtherModuleDependencyTag from inside VisitDirectDeps
if logicModule == m.visiting.module.logicModule {
return m.visiting.tag
if logicModule == m.visitingDep.module.logicModule {
return m.visitingDep.tag
}
for _, dep := range m.module.directDeps {
for _, dep := range m.visitingParent.directDeps {
if dep.module.logicModule == logicModule {
return dep.tag
}
@ -272,46 +273,91 @@ func (m *baseModuleContext) VisitDirectDeps(visit func(Module)) {
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitDirectDeps(%s, %s) for dependency %s",
m.module, funcName(visit), m.visiting.module))
m.module, funcName(visit), m.visitingDep.module))
}
}()
m.visitingParent = m.module
for _, dep := range m.module.directDeps {
m.visiting = dep
m.visitingDep = dep
visit(dep.module.logicModule)
}
m.visiting = depInfo{}
m.visitingParent = nil
m.visitingDep = depInfo{}
}
func (m *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitDirectDepsIf(%s, %s, %s) for dependency %s",
m.module, funcName(pred), funcName(visit), m.visiting.module))
m.module, funcName(pred), funcName(visit), m.visitingDep.module))
}
}()
m.visitingParent = m.module
for _, dep := range m.module.directDeps {
m.visiting = dep
m.visitingDep = dep
if pred(dep.module.logicModule) {
visit(dep.module.logicModule)
}
}
m.visiting = depInfo{}
m.visitingParent = nil
m.visitingDep = depInfo{}
}
func (m *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) {
m.context.visitDepsDepthFirst(m.module, visit)
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s",
m.module, funcName(visit), m.visitingDep.module))
}
}()
m.context.walkDeps(m.module, nil, func(dep depInfo, parent *moduleInfo) {
m.visitingParent = parent
m.visitingDep = dep
visit(dep.module.logicModule)
})
m.visitingParent = nil
m.visitingDep = depInfo{}
}
func (m *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool,
visit func(Module)) {
m.context.visitDepsDepthFirstIf(m.module, pred, visit)
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s",
m.module, funcName(pred), funcName(visit), m.visitingDep.module))
}
}()
m.context.walkDeps(m.module, nil, func(dep depInfo, parent *moduleInfo) {
if pred(dep.module.logicModule) {
m.visitingParent = parent
m.visitingDep = dep
visit(dep.module.logicModule)
}
})
m.visitingParent = nil
m.visitingDep = depInfo{}
}
func (m *baseModuleContext) WalkDeps(visit func(Module, Module) bool) {
m.context.walkDeps(m.module, visit)
m.context.walkDeps(m.module, func(dep depInfo, parent *moduleInfo) bool {
m.visitingParent = parent
m.visitingDep = dep
return visit(dep.module.logicModule, parent.logicModule)
}, nil)
m.visitingParent = nil
m.visitingDep = depInfo{}
}
func (m *moduleContext) ModuleSubDir() string {

154
visit_test.go Normal file
View file

@ -0,0 +1,154 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package blueprint
import (
"fmt"
"testing"
)
type visitModule struct {
properties struct {
Visit []string
VisitDepsDepthFirst string `blueprint:"mutated"`
VisitDepsDepthFirstIf string `blueprint:"mutated"`
VisitDirectDeps string `blueprint:"mutated"`
VisitDirectDepsIf string `blueprint:"mutated"`
}
}
func newVisitModule() (Module, []interface{}) {
m := &visitModule{}
return m, []interface{}{&m.properties}
}
func (f *visitModule) GenerateBuildActions(ModuleContext) {
}
type visitTag struct {
BaseDependencyTag
}
var visitTagDep visitTag
func visitDepsMutator(ctx BottomUpMutatorContext) {
if m, ok := ctx.Module().(*visitModule); ok {
ctx.AddDependency(ctx.Module(), visitTagDep, m.properties.Visit...)
}
}
func visitMutator(ctx TopDownMutatorContext) {
if m, ok := ctx.Module().(*visitModule); ok {
ctx.VisitDepsDepthFirst(func(dep Module) {
if ctx.OtherModuleDependencyTag(dep) != visitTagDep {
panic(fmt.Errorf("unexpected dependency tag on %q", ctx.OtherModuleName(dep)))
}
m.properties.VisitDepsDepthFirst = m.properties.VisitDepsDepthFirst + ctx.OtherModuleName(dep)
})
ctx.VisitDepsDepthFirstIf(func(dep Module) bool {
return ctx.OtherModuleName(dep) != "B"
}, func(dep Module) {
m.properties.VisitDepsDepthFirstIf = m.properties.VisitDepsDepthFirstIf + ctx.OtherModuleName(dep)
})
ctx.VisitDirectDeps(func(dep Module) {
m.properties.VisitDirectDeps = m.properties.VisitDirectDeps + ctx.OtherModuleName(dep)
})
ctx.VisitDirectDepsIf(func(dep Module) bool {
return ctx.OtherModuleName(dep) != "B"
}, func(dep Module) {
m.properties.VisitDirectDepsIf = m.properties.VisitDirectDepsIf + ctx.OtherModuleName(dep)
})
}
}
// A
// |
// B
// |\
// C \
// \|
// D
// |
// E
func setupVisitTest(t *testing.T) *Context {
ctx := NewContext()
ctx.RegisterModuleType("visit_module", newVisitModule)
ctx.RegisterBottomUpMutator("visit_deps", visitDepsMutator)
ctx.RegisterTopDownMutator("visit", visitMutator)
ctx.MockFileSystem(map[string][]byte{
"Blueprints": []byte(`
visit_module {
name: "A",
visit: ["B"],
}
visit_module {
name: "B",
visit: ["C", "D"],
}
visit_module {
name: "C",
visit: ["D"],
}
visit_module {
name: "D",
visit: ["E"],
}
visit_module {
name: "E",
}
`),
})
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
if len(errs) > 0 {
t.Errorf("unexpected parse errors:")
for _, err := range errs {
t.Errorf(" %s", err)
}
t.FailNow()
}
errs = ctx.ResolveDependencies(nil)
if len(errs) > 0 {
t.Errorf("unexpected dep errors:")
for _, err := range errs {
t.Errorf(" %s", err)
}
t.FailNow()
}
return ctx
}
func TestVisit(t *testing.T) {
ctx := setupVisitTest(t)
topModule := ctx.moduleGroups["A"].modules[0].logicModule.(*visitModule)
assertString(t, topModule.properties.VisitDepsDepthFirst, "EDCB")
assertString(t, topModule.properties.VisitDepsDepthFirstIf, "EDC")
assertString(t, topModule.properties.VisitDirectDeps, "B")
assertString(t, topModule.properties.VisitDirectDepsIf, "")
}
func assertString(t *testing.T, got, expected string) {
if got != expected {
t.Errorf("expected %q got %q", expected, got)
}
}