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  }