Skip to content

Commit edd395c

Browse files
committed
Enhance test coverage
1 parent d8ba941 commit edd395c

File tree

3 files changed

+218
-7
lines changed

3 files changed

+218
-7
lines changed

common_test.go

+58-4
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,66 @@ func (tbw *testBufWriter) Len() int {
3333
return tbw.buf.Len()
3434
}
3535

36-
// A Writer which can be configured to return a truncated Write() and/or an error.
36+
type testTTWresult struct {
37+
desc string
38+
len int // 0: return 0, -1: return len(p), otherwise use len
39+
err error
40+
}
41+
42+
// A Writer which can be configured to return a truncated Write() and/or an error. Each
43+
// call to Write pops a result off the stack and returns the results. If the stack is
44+
// empty the Write returns success. All successful writes are stored in buf.
3745
type testTruncateWriter struct {
38-
len int
39-
err error
46+
index int
47+
results []testTTWresult
48+
buf bytes.Buffer
49+
}
50+
51+
func (ttw *testTruncateWriter) String() string {
52+
return ttw.buf.String()
4053
}
4154

55+
// Place this result at the bottom of the stack
56+
func (ttw *testTruncateWriter) append(desc string, len int, err error) {
57+
ttw.results = append(ttw.results, testTTWresult{desc, len, err})
58+
}
59+
60+
// Pop the next results and return them to caller. A zero length returns zero, a positive
61+
// length means return that value and -1 means return the length of the slice.
4262
func (ttw *testTruncateWriter) Write(p []byte) (n int, err error) {
43-
return ttw.len, ttw.err
63+
ttw.index++
64+
if len(ttw.results) == 0 { // If stack is empty, return complete success
65+
return ttw.buf.Write(p)
66+
}
67+
68+
r := ttw.results[0] // Pop off stack
69+
ttw.results = ttw.results[1:]
70+
err = r.err
71+
switch r.len {
72+
case 0:
73+
n = 0
74+
case -1:
75+
n = len(p)
76+
default:
77+
n = r.len
78+
if n > len(p) {
79+
n = len(p)
80+
}
81+
}
82+
83+
if n > 0 {
84+
ttw.buf.Write(p[:n])
85+
}
86+
87+
return
88+
}
89+
90+
func (ttw *testTruncateWriter) close() {
91+
}
92+
93+
func (ttw *testTruncateWriter) getNext() writer {
94+
return nil
95+
}
96+
97+
func (ttw *testTruncateWriter) setNext(w writer) {
4498
}

queue_test.go

+53
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package parallel
22

33
import (
4+
"errors"
45
"testing"
56
"time"
67
)
@@ -149,3 +150,55 @@ func TestQueueBlock(t *testing.T) {
149150
outQ.close()
150151
errQ.close()
151152
}
153+
154+
// First error should be returned and all subsequent errors ignored. Once a write fails,
155+
// no further writes to the io.Writer should occur.
156+
func TestQueueTransferOut(t *testing.T) {
157+
ob := &testTruncateWriter{}
158+
eb := &testTruncateWriter{}
159+
outQ, errQ := newQueue(false, 0, ob, eb)
160+
outQ.Write([]byte{'a', 'b', 'c', '\n'})
161+
outQ.Write([]byte{'x', 'y', 'z', '\n'})
162+
errQ.Write([]byte{'A', 'B', 'C', '\n'})
163+
errQ.Write([]byte{'X', 'Y', 'Z', '\n'})
164+
ob.append("stdout Write Fail", 1, errors.New("Stdout write failed"))
165+
eb.append("stderr should not be visible", 2, errors.New("Stderr write Failed"))
166+
e := outQ.cq.buf.transfer(ob, eb)
167+
if e == nil {
168+
t.Error("Expected error return from transfer")
169+
}
170+
res := ob.String()
171+
if res != "a" { // Should be one byte
172+
t.Error("Expected stdout to be 'a', not", res)
173+
}
174+
res = eb.String()
175+
if res != "AB" { // Should be two bytes
176+
t.Error("Expected stderr to be 'AB', not", res)
177+
}
178+
}
179+
180+
// All of stdout should be written, but stderr should be truncated and return an error.
181+
func TestQueueTransferErr(t *testing.T) {
182+
ob := &testTruncateWriter{}
183+
eb := &testTruncateWriter{}
184+
outQ, errQ := newQueue(false, 0, ob, eb)
185+
outQ.Write([]byte{'a'})
186+
outQ.Write([]byte{'b'})
187+
outQ.Write([]byte{'c'})
188+
outQ.Write([]byte{'d'})
189+
errQ.Write([]byte{'A', 'B', 'C', '\n'})
190+
errQ.Write([]byte{'X', 'Y', 'Z', '\n'})
191+
eb.append("stderr Write Fail", 3, errors.New("Stderr write failed"))
192+
e := outQ.cq.buf.transfer(ob, eb)
193+
if e == nil {
194+
t.Error("Expected error return from transfer")
195+
}
196+
res := ob.String()
197+
if res != "abcd" { // Should be one byte
198+
t.Error("Expected stdout to be 'abcd', not", res)
199+
}
200+
res = eb.String()
201+
if res != "ABC" { // Should be three bytes
202+
t.Error("Expected stderr to be 'ABC', not", res)
203+
}
204+
}

tagger_test.go

+107-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package parallel
22

33
import (
4+
"errors"
45
"testing"
56
)
67

@@ -24,7 +25,7 @@ func TestTaggerEmpty(t *testing.T) {
2425
}
2526
}
2627

27-
// Test tagger prepend logic with a whole line data
28+
// Prepend with a whole line data
2829
func TestTaggerSimple(t *testing.T) {
2930
var buf testBufWriter
3031
wtr := newTagger(&buf, []byte("host1: "))
@@ -66,7 +67,7 @@ func TestTaggerNoTrailingNL(t *testing.T) {
6667
}
6768
}
6869

69-
// Test tagger prepend logic with partial lines
70+
// Prepend with partial lines
7071
func TestTaggerPartialWrites(t *testing.T) {
7172
var buf testBufWriter
7273
wtr := newTagger(&buf, []byte("host1: "))
@@ -89,7 +90,7 @@ func TestTaggerPartialWrites(t *testing.T) {
8990
}
9091
}
9192

92-
// Test tagger with zero-length writes
93+
// Zero-length writes
9394
func TestTaggerWriteZero(t *testing.T) {
9495
var buf testBufWriter
9596
wtr := newTagger(&buf, []byte("host1: "))
@@ -109,3 +110,106 @@ func TestTaggerWriteZero(t *testing.T) {
109110
t.Error("Unexpected modification. \nExp", exp, "\nactual", act)
110111
}
111112
}
113+
114+
// Data is zero length
115+
func TestTaggerIOErrorsW0(t *testing.T) {
116+
buf := &testTruncateWriter{}
117+
wtr := newTagger(buf, []byte(""))
118+
b, err := wtr.Write([]byte{})
119+
if b != 0 || err != nil {
120+
t.Error("Expected zero and nil, not", b, err)
121+
}
122+
}
123+
124+
// Tag is zero length so return downstream write results
125+
func TestTaggerIOErrorsW1(t *testing.T) {
126+
buf := &testTruncateWriter{}
127+
buf.append("Error on first write", -1, errors.New("W1 error"))
128+
wtr := newTagger(buf, []byte(""))
129+
b, err := wtr.Write([]byte{'a'})
130+
if b != 1 || err == nil {
131+
t.Error("Expected one and error, not", b)
132+
}
133+
}
134+
135+
// Error with tag pending on first line
136+
func TestTaggerIOErrorsW2(t *testing.T) {
137+
buf := &testTruncateWriter{}
138+
wtr := newTagger(buf, []byte("W2Tag"))
139+
buf.append("Error on first write", 0, errors.New("Tag Error"))
140+
b, err := wtr.Write([]byte{'a', '\n', 'b', '\n'})
141+
if b != 4 { // User bytes written is unaffected
142+
t.Error("Expected 4 bytes written, not", b)
143+
}
144+
if err == nil || err.Error() != "Tag Error" { // But error should be present
145+
t.Error("Expected Tag Error return, not", err)
146+
}
147+
}
148+
149+
// Error on writing of first line
150+
func TestTaggerIOErrorsW3(t *testing.T) {
151+
buf := &testTruncateWriter{}
152+
wtr := newTagger(buf, []byte("W3Tag"))
153+
buf.append("W1 is good", -1, nil)
154+
buf.append("W3 is error", -1, errors.New("W3 error"))
155+
156+
data := []byte{'a', 'b', 'c', 'd', '\n', 'A', 'B', '\n'}
157+
b, err := wtr.Write(data)
158+
if b != len(data) {
159+
t.Error("Expected W3 to return", len(data), "not", b)
160+
}
161+
if err == nil || err.Error() != "W3 error" {
162+
t.Error("Expected 'W3 error', not", err)
163+
}
164+
}
165+
166+
// Error on writing NL between split lines
167+
func TestTaggerIOErrorsW4(t *testing.T) {
168+
buf := &testTruncateWriter{}
169+
wtr := newTagger(buf, []byte("W4Tag"))
170+
buf.append("W2Tag", -1, nil)
171+
buf.append("W3Line 1 'abcd'", -1, nil)
172+
buf.append("W4 NL fails", 0, errors.New("W4 NL failed"))
173+
174+
data := []byte{'a', 'b', 'c', 'd', '\n', 'A', 'B', '\n'}
175+
b, err := wtr.Write(data)
176+
if b != len(data)-1 { // Minus the NL
177+
t.Error("Expected W4 to return", len(data)-1, "not", b)
178+
}
179+
if err == nil || err.Error() != "W4 NL failed" {
180+
t.Error("Expected 'W4 NL failed', not", err)
181+
}
182+
}
183+
184+
// Error on writing tag on last line
185+
func TestTaggerIOErrorsW5(t *testing.T) {
186+
buf := &testTruncateWriter{}
187+
wtr := newTagger(buf, []byte("W5Tag"))
188+
buf.append("W5 Tag fails", 2, errors.New("W5 Tag failed"))
189+
190+
data := []byte{'a', 'b', 'c'} // Last line
191+
b, err := wtr.Write(data)
192+
if b != 3 { // Tag write failed, but data write succeeds
193+
t.Error("Expected W5 to return", 3, "not", b)
194+
}
195+
if err == nil || err.Error() != "W5 Tag failed" {
196+
t.Error("Expected 'W5 Tag failed', not", err)
197+
}
198+
}
199+
200+
// Error on writing data on last line
201+
func TestTaggerIOErrorsW6(t *testing.T) {
202+
buf := &testTruncateWriter{}
203+
wtr := newTagger(buf, []byte("W5Tag"))
204+
buf.append("W5 Tag ok", -1, nil)
205+
buf.append("W6 LL fails", 2, errors.New("W6 LL failed"))
206+
207+
data := []byte{'a', 'b', 'c'} // Last line
208+
b, err := wtr.Write(data)
209+
if b != 2 { // Tag write ok, but data write failed
210+
t.Error("Expected W6 to return", 2, "not", b)
211+
}
212+
if err == nil || err.Error() != "W6 LL failed" {
213+
t.Error("Expected 'W6 LL failed', not", err)
214+
}
215+
}

0 commit comments

Comments
 (0)