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 }