blob: 066e014dbf72abd32ae99b3c153ffe79cbf47d8a [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"bytes"
"context"
"fmt"
"io"
"sync"
"testing"
)
type failRunner struct {
stdout string
stderr string
err string
}
func (f *failRunner) Run(ctx context.Context, command []string, stdout, stderr io.Writer) error {
io.WriteString(stdout, f.stdout)
io.WriteString(stderr, f.stderr)
return fmt.Errorf("%s", f.err)
}
func TestExpectedStdout(t *testing.T) {
// Test that on error we get the expected Stdout result
var stderr bytes.Buffer
var stdout bytes.Buffer
ctx := context.Background()
br := newBatchRunner(ctx, &failRunner{"foo", "bar", "baz"}, 1)
br.Enqueue([]string{}, &stdout, &stderr)
batchErr := br.Wait()
if batchErr == nil {
t.Error("expected non-nil error on Wait but got nil")
}
if stdout.String() != "foo" {
t.Error("expected stdout to be \"foo\" but got ", stdout.String())
}
if stderr.String() != "bar" {
t.Error("expected stderr to be \"bar\" but got ", stderr.String())
}
if batchErr.Error() != "baz" {
t.Error("expected batchErr to be \"baz\" but got ", batchErr.Error())
}
}
type ThreadSafeBuffer struct {
buf bytes.Buffer
lock sync.Mutex
}
func (t *ThreadSafeBuffer) Write(data []byte) (int, error) {
defer t.lock.Unlock()
t.lock.Lock()
return t.buf.Write(data)
}
func (t *ThreadSafeBuffer) String() string {
return t.buf.String()
}
type mockRunner struct{}
func (m mockRunner) Run(ctx context.Context, command []string, stdout, stderr io.Writer) error {
for _, str := range command {
io.WriteString(stderr, str)
}
return nil
}
func TestEnqueueWait(t *testing.T) {
// Do a standard smoke test of the basic functionality.
var stderr ThreadSafeBuffer
var stdout bytes.Buffer
ctx := context.Background()
// Set max batch size of 3
br := newBatchRunner(ctx, mockRunner{}, 3)
// Enqueue more than 3 tasks.
for i := 0; i < 10; i++ {
br.Enqueue([]string{"a"}, &stdout, &stderr)
}
if err := br.Wait(); err != nil {
t.Error(err)
}
// This is also how I feel inside.
expected := "aaaaaaaaaa"
if stderr.String() != expected {
t.Error("expected stderr to be \""+expected+"\" but got ", stderr.String())
}
}
type ignoreWriter struct{}
func (i ignoreWriter) Write(data []byte) (int, error) {
return len(data), nil
}
func TestEnqueuePanic(t *testing.T) {
// Test that Enqueue panics if called after Wait.
defer func() {
if r := recover(); r == nil {
t.Error("expected panic but got none")
}
}()
ctx := context.Background()
br := newBatchRunner(ctx, mockRunner{}, 1)
if err := br.Wait(); err != nil {
t.Error(err)
}
br.Enqueue([]string{}, ignoreWriter{}, ignoreWriter{})
}
type loopRunner struct{}
func (m loopRunner) Run(ctx context.Context, command []string, stdout, stderr io.Writer) error {
<-ctx.Done()
return nil
}
func TestCancelJob(t *testing.T) {
// Test that canceling a job unblocks it.
var stderr bytes.Buffer
var stdout bytes.Buffer
ctx, cancel := context.WithCancel(context.Background())
br := newBatchRunner(ctx, loopRunner{}, 2)
br.Enqueue([]string{}, &stdout, &stderr)
cancel()
if err := br.Wait(); err != nil {
t.Error(err)
}
if len(stderr.Bytes()) != 0 {
t.Error("expected no output on stderr but got: ", stderr.String())
}
if len(stdout.Bytes()) != 0 {
t.Error("expected no output on stdout but got: ", stdout.String())
}
}