This commit is contained in:
Adrien Delorme 2019-05-02 14:41:44 +02:00
parent 2f577e60b6
commit c1527d9b47
1 changed files with 37 additions and 35 deletions

View File

@ -4,32 +4,47 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"math"
"path/filepath" "path/filepath"
"sync" "sync"
"testing" "testing"
"golang.org/x/sync/errgroup"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
// NewParallelTestBuilder will return a New ParallelTestBuilder whose first run
// will lock until unlockOnce is closed and that will unlock after `runs`
// builds
func NewParallelTestBuilder(runs int) *ParallelTestBuilder {
pb := &ParallelTestBuilder{
unlockOnce: make(chan interface{}),
}
pb.wg.Add(runs)
return pb
}
// The ParallelTestBuilder's first run will lock
type ParallelTestBuilder struct { type ParallelTestBuilder struct {
Prepared int once sync.Once
Built int unlockOnce chan interface{}
wg *sync.WaitGroup
m *sync.Mutex wg sync.WaitGroup
} }
func (b *ParallelTestBuilder) Prepare(raws ...interface{}) ([]string, error) { func (b *ParallelTestBuilder) Prepare(raws ...interface{}) ([]string, error) {
b.Prepared++
return nil, nil return nil, nil
} }
func (b *ParallelTestBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { func (b *ParallelTestBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
ui.Say(fmt.Sprintf("count: %d", b.Built)) b.once.Do(func() {
b.Built++ ui.Say("locking build")
<-b.unlockOnce
b.wg.Add(1) // avoid a panic
})
ui.Say("building")
b.wg.Done() b.wg.Done()
b.m.Lock()
b.m.Unlock()
return nil, nil return nil, nil
} }
@ -57,42 +72,29 @@ func testMetaParallel(t *testing.T, builder *ParallelTestBuilder) Meta {
} }
func TestBuildParallel(t *testing.T) { func TestBuildParallel(t *testing.T) {
defer cleanup() // testfile that running 6 builds, with first one locks 'forever', other
// builds should go through.
m := &sync.Mutex{} b := NewParallelTestBuilder(5)
m.Lock()
expected := 2
wg := &sync.WaitGroup{}
wg.Add(expected)
b := &ParallelTestBuilder{
wg: wg,
m: m,
}
c := &BuildCommand{ c := &BuildCommand{
Meta: testMetaParallel(t, b), Meta: testMetaParallel(t, b),
} }
args := []string{ args := []string{
fmt.Sprintf("-parallel=%d", expected), fmt.Sprintf("-parallel=2"),
filepath.Join(testFixture("parallel"), "template.json"), filepath.Join(testFixture("parallel"), "template.json"),
} }
go func(t *testing.T, c *BuildCommand) { wg := errgroup.Group{}
wg.Go(func() error {
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
fatalCommand(t, c.Meta) fatalCommand(t, c.Meta)
} }
}(t, c) return nil
})
wg.Wait() b.wg.Wait() // ran 5 times
if b.Prepared != 6 { close(b.unlockOnce) // unlock locking one
t.Errorf("Expected all builds to be prepared, was %d", b.Prepared) wg.Wait() // wait for termination
}
if b.Built != expected {
t.Errorf("Expected only %d running/completed builds, was %d", expected, b.Built)
}
m.Unlock()
wg.Add(math.MaxInt32)
} }