github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/build/build_step_stress_test.go (about) 1 // Stress test around the build step stuff, specifically trying to 2 // identify concurrent map read / writes. 3 4 package build 5 6 import ( 7 "fmt" 8 "sync" 9 "testing" 10 11 "github.com/stretchr/testify/assert" 12 13 "core" 14 ) 15 16 const size = 1000 17 const numWorkers = 10 18 19 var state *core.BuildState 20 21 func TestBuildLotsOfTargets(t *testing.T) { 22 config, _ := core.ReadConfigFiles(nil, "") 23 state = core.NewBuildState(numWorkers, nil, 4, config) 24 state.Parser = &fakeParser{ 25 PostBuildFunctions: buildFunctionMap{}, 26 } 27 pkg := core.NewPackage("pkg") 28 state.Graph.AddPackage(pkg) 29 for i := 1; i <= size; i++ { 30 addTarget(state, i) 31 } 32 var wg sync.WaitGroup 33 wg.Add(numWorkers) 34 for i := 0; i < numWorkers; i++ { 35 go func(i int) { 36 please(i, state) 37 wg.Done() 38 }(i) 39 } 40 // Consume and discard any results 41 go func() { 42 for result := range state.Results { 43 assert.NotEqual(t, core.TargetBuildFailed, result.Status) 44 log.Info("%s", result.Description) 45 } 46 }() 47 state.TaskDone() // Initial target adding counts as one. 48 wg.Wait() 49 } 50 51 func addTarget(state *core.BuildState, i int) *core.BuildTarget { 52 // Create and add a new target, with a parent and a dependency. 53 target := core.NewBuildTarget(label(i)) 54 target.IsFilegroup = true // Will mean it doesn't have to shell out to anything. 55 target.SetState(core.Active) 56 state.Graph.AddTarget(target) 57 if i <= size { 58 if i > 10 { 59 target.Flakiness = i // Stash this here, will be useful later. 60 state.Parser.(*fakeParser).PostBuildFunctions[target] = postBuild 61 } 62 if i < size/10 { 63 for j := 0; j < 10; j++ { 64 dep := label(i*10 + j) 65 log.Info("Adding dependency %s -> %s", target.Label, dep) 66 target.AddDependency(dep) 67 state.Graph.AddDependency(target.Label, dep) 68 } 69 } else { 70 // These are buildable now 71 state.AddPendingBuild(target.Label, false) 72 } 73 } 74 state.AddActiveTarget() 75 return target 76 } 77 78 func label(i int) core.BuildLabel { 79 return core.ParseBuildLabel(fmt.Sprintf("//pkg:target%d", i), "") 80 } 81 82 // please mimics the core build 'loop' from src/please.go. 83 func please(tid int, state *core.BuildState) { 84 for { 85 label, _, t := state.NextTask() 86 switch t { 87 case core.Stop, core.Kill: 88 return 89 case core.Build: 90 Build(tid, state, label) 91 default: 92 panic(fmt.Sprintf("unexpected task type: %d", t)) 93 } 94 state.TaskDone() 95 } 96 } 97 98 // Post-build function that adds new targets & ties in dependencies. 99 func postBuild(target *core.BuildTarget, out string) error { 100 // Add a target corresponding to this one to its 'parent' 101 if target.Flakiness == 0 { 102 return fmt.Errorf("shouldn't be calling a post-build function on %s", target.Label) 103 } 104 parent := label(target.Flakiness / 10) 105 newTarget := addTarget(state, target.Flakiness+size) 106 107 // This mimics what interpreter.go does. 108 state.Graph.TargetOrDie(parent).AddMaybeExportedDependency(newTarget.Label, false, false) 109 state.Graph.AddDependency(parent, newTarget.Label) 110 return nil 111 } 112 113 type buildFunctionMap map[*core.BuildTarget]func(*core.BuildTarget, string) error 114 115 type fakeParser struct { 116 PostBuildFunctions buildFunctionMap 117 } 118 119 func (fake *fakeParser) ParseFile(state *core.BuildState, pkg *core.Package, filename string) error { 120 return nil 121 } 122 123 func (fake *fakeParser) RunPreBuildFunction(threadID int, state *core.BuildState, target *core.BuildTarget) error { 124 return nil 125 } 126 127 func (fake *fakeParser) RunPostBuildFunction(threadID int, state *core.BuildState, target *core.BuildTarget, output string) error { 128 if f, present := fake.PostBuildFunctions[target]; present { 129 return f(target, output) 130 } 131 return nil 132 } 133 134 func (fake *fakeParser) UndeferAnyParses(state *core.BuildState, target *core.BuildTarget) { 135 }