github.com/stevenmatthewt/agent@v3.5.4+incompatible/process/process_test.go (about)

     1  package process_test
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"reflect"
     7  	"regexp"
     8  	"strings"
     9  	"sync"
    10  	"sync/atomic"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/buildkite/agent/process"
    15  )
    16  
    17  const longTestOutput = `+++ My header
    18  llamas
    19  and more llamas
    20  a very long line a very long line a very long line a very long line a very long line a very long line a very long line a very long line a very long line a very long line a very long line a very long line a very long line a very long line
    21  and some alpacas
    22  `
    23  
    24  func TestProcessRunsAndCallsStartCallback(t *testing.T) {
    25  	var started int32
    26  
    27  	p := process.Process{
    28  		Script: []string{os.Args[0]},
    29  		Env:    []string{"TEST_MAIN=tester"},
    30  		StartCallback: func() {
    31  			atomic.AddInt32(&started, 1)
    32  		},
    33  		LineCallback:       func(s string) {},
    34  		LinePreProcessor:   func(s string) string { return s },
    35  		LineCallbackFilter: func(s string) bool { return false },
    36  	}
    37  
    38  	if err := p.Start(); err != nil {
    39  		t.Fatal(err)
    40  	}
    41  
    42  	if startedVal := atomic.LoadInt32(&started); startedVal != 1 {
    43  		t.Fatalf("Expected started to be 1, got %d", startedVal)
    44  	}
    45  
    46  	if exitStatus := p.ExitStatus; exitStatus != "0" {
    47  		t.Fatalf("Expected ExitStatus of 0, got %v", exitStatus)
    48  	}
    49  
    50  	output := p.Output()
    51  	if output != string(longTestOutput) {
    52  		t.Fatalf("Output was unexpected:\nWanted: %q\nGot:    %q\n", longTestOutput, output)
    53  	}
    54  }
    55  
    56  func TestProcessCallsLineCallbacksForEachOutputLine(t *testing.T) {
    57  	var lineCounter int32
    58  	var lines []string
    59  	var linesLock sync.Mutex
    60  
    61  	p := process.Process{
    62  		Script:        []string{os.Args[0]},
    63  		Env:           []string{"TEST_MAIN=tester"},
    64  		StartCallback: func() {},
    65  		LineCallback: func(s string) {
    66  			linesLock.Lock()
    67  			defer linesLock.Unlock()
    68  			lines = append(lines, s)
    69  		},
    70  		LinePreProcessor: func(s string) string {
    71  			lineNumber := atomic.AddInt32(&lineCounter, 1)
    72  			return fmt.Sprintf("#%d: chars %d", lineNumber, len(s))
    73  		},
    74  		LineCallbackFilter: func(s string) bool {
    75  			return true
    76  		},
    77  	}
    78  
    79  	if err := p.Start(); err != nil {
    80  		t.Fatal(err)
    81  	}
    82  
    83  	linesLock.Lock()
    84  
    85  	var expected = []string{
    86  		`#1: chars 13`,
    87  		`#2: chars 6`,
    88  		`#3: chars 15`,
    89  		`#4: chars 237`,
    90  		`#5: chars 16`,
    91  	}
    92  
    93  	if !reflect.DeepEqual(expected, lines) {
    94  		t.Fatalf("Lines was unexpected:\nWanted: %v\nGot:    %v\n", expected, lines)
    95  	}
    96  }
    97  
    98  func TestProcessPrependsLinesWithTimestamps(t *testing.T) {
    99  	p := process.Process{
   100  		Script:             []string{os.Args[0]},
   101  		Env:                []string{"TEST_MAIN=tester"},
   102  		StartCallback:      func() {},
   103  		LineCallback:       func(s string) {},
   104  		LinePreProcessor:   func(s string) string { return s },
   105  		LineCallbackFilter: func(s string) bool { return strings.HasPrefix(s, "+++") },
   106  		Timestamp:          true,
   107  	}
   108  
   109  	if err := p.Start(); err != nil {
   110  		t.Fatal(err)
   111  	}
   112  
   113  	lines := strings.Split(strings.TrimSpace(p.Output()), "\n")
   114  
   115  	if lines[0] != `+++ My header` {
   116  		t.Fatalf("Expected first line to be %q, got %q", `+++ My header`, lines[0])
   117  	}
   118  
   119  	tsRegex := regexp.MustCompile(`^\[.+?\]`)
   120  
   121  	for _, line := range lines[1:] {
   122  		if !tsRegex.MatchString(line) {
   123  			t.Fatalf("Line doesn't start with a timestamp: %s", line)
   124  		}
   125  	}
   126  }
   127  
   128  func TestProcessOutputIsSafeFromRaces(t *testing.T) {
   129  	var counter int32
   130  
   131  	p := process.Process{
   132  		Script:             []string{os.Args[0]},
   133  		Env:                []string{"TEST_MAIN=tester"},
   134  		LineCallback:       func(s string) {},
   135  		LinePreProcessor:   func(s string) string { return s },
   136  		LineCallbackFilter: func(s string) bool { return false },
   137  	}
   138  
   139  	// the job_runner has a for loop that calls IsRunning and Output, so this checks those are safe from races
   140  	p.StartCallback = func() {
   141  		for p.IsRunning() {
   142  			_ = p.Output()
   143  			atomic.AddInt32(&counter, 1)
   144  			time.Sleep(time.Millisecond * 10)
   145  		}
   146  	}
   147  
   148  	if err := p.Start(); err != nil {
   149  		t.Fatal(err)
   150  	}
   151  
   152  	output := p.Output()
   153  	if output != string(longTestOutput) {
   154  		t.Fatalf("Output was unexpected:\nWanted: %q\nGot:    %q\n", longTestOutput, output)
   155  	}
   156  
   157  	if counterVal := atomic.LoadInt32(&counter); counterVal < 10 {
   158  		t.Fatalf("Expected counter to be at least 10, got %d", counterVal)
   159  	}
   160  }
   161  
   162  // Invoked by `go test`, switch between helper and running tests based on env
   163  func TestMain(m *testing.M) {
   164  	switch os.Getenv("TEST_MAIN") {
   165  	case "tester":
   166  		for _, line := range strings.Split(strings.TrimSuffix(longTestOutput, "\n"), "\n") {
   167  			fmt.Printf("%s\n", line)
   168  			time.Sleep(time.Millisecond * 20)
   169  		}
   170  		os.Exit(0)
   171  	default:
   172  		os.Exit(m.Run())
   173  	}
   174  }