1// This tests WatchdogReader 2 3package swift 4 5import ( 6 "bytes" 7 "io" 8 "io/ioutil" 9 "testing" 10 "time" 11) 12 13// Uses testReader from timeout_reader_test.go 14 15func testWatchdogReaderTimeout(t *testing.T, initialTimeout, watchdogTimeout time.Duration, expectedTimeout bool) { 16 test := newTestReader(3, 10*time.Millisecond) 17 timer, firedChan := setupTimer(initialTimeout) 18 wr := newWatchdogReader(test, watchdogTimeout, timer) 19 b, err := ioutil.ReadAll(wr) 20 if err != nil || string(b) != "AAA" { 21 t.Fatalf("Bad read %s %s", err, b) 22 } 23 checkTimer(t, firedChan, expectedTimeout) 24} 25 26func setupTimer(initialTimeout time.Duration) (timer *time.Timer, fired <-chan bool) { 27 timer = time.NewTimer(initialTimeout) 28 firedChan := make(chan bool) 29 started := make(chan bool) 30 go func() { 31 started <- true 32 select { 33 case <-timer.C: 34 firedChan <- true 35 } 36 }() 37 <-started 38 return timer, firedChan 39} 40 41func checkTimer(t *testing.T, firedChan <-chan bool, expectedTimeout bool) { 42 fired := false 43 select { 44 case fired = <-firedChan: 45 default: 46 } 47 if expectedTimeout { 48 if !fired { 49 t.Fatal("Timer should have fired") 50 } 51 } else { 52 if fired { 53 t.Fatal("Timer should not have fired") 54 } 55 } 56} 57 58func TestWatchdogReaderNoTimeout(t *testing.T) { 59 testWatchdogReaderTimeout(t, 100*time.Millisecond, 100*time.Millisecond, false) 60} 61 62func TestWatchdogReaderTimeout(t *testing.T) { 63 testWatchdogReaderTimeout(t, 5*time.Millisecond, 5*time.Millisecond, true) 64} 65 66func TestWatchdogReaderNoTimeoutShortInitial(t *testing.T) { 67 testWatchdogReaderTimeout(t, 5*time.Millisecond, 100*time.Millisecond, false) 68} 69 70func TestWatchdogReaderTimeoutLongInitial(t *testing.T) { 71 testWatchdogReaderTimeout(t, 100*time.Millisecond, 5*time.Millisecond, true) 72} 73 74//slowReader simulates reading from a slow network connection by introducing a delay 75//in each Read() proportional to the amount of bytes read. 76type slowReader struct { 77 reader io.Reader 78 delayPerByte time.Duration 79} 80 81func (r *slowReader) Read(p []byte) (n int, err error) { 82 n, err = r.reader.Read(p) 83 if n > 0 { 84 time.Sleep(time.Duration(n) * r.delayPerByte) 85 } 86 return 87} 88 89//This test verifies that the watchdogReader's timeout is not triggered by data 90//that comes in very slowly. (It should only be triggered if no data arrives at 91//all.) 92func TestWatchdogReaderOnSlowNetwork(t *testing.T) { 93 byteString := make([]byte, 8*watchdogChunkSize) 94 reader := &slowReader{ 95 reader: bytes.NewReader(byteString), 96 //reading everything at once would take 100 ms, which is longer than the 97 //watchdog timeout below 98 delayPerByte: 200 * time.Millisecond / time.Duration(len(byteString)), 99 } 100 101 timer, firedChan := setupTimer(100 * time.Millisecond) 102 wr := newWatchdogReader(reader, 190*time.Millisecond, timer) 103 104 //use io.ReadFull instead of ioutil.ReadAll here because ReadAll already does 105 //some chunking that would keep this testcase from failing 106 b := make([]byte, len(byteString)) 107 n, err := io.ReadFull(wr, b) 108 if err != nil || n != len(b) || !bytes.Equal(b, byteString) { 109 t.Fatalf("Bad read %s %d", err, n) 110 } 111 112 checkTimer(t, firedChan, false) 113} 114 115//This test verifies that the watchdogReader's chunking logic does not mess up 116//the byte strings that are read. 117func TestWatchdogReaderValidity(t *testing.T) { 118 byteString := []byte("abcdefghij") 119 //make a reader with a non-standard chunk size (1 MiB would be much too huge 120 //to comfortably look at the bytestring that comes out of the reader) 121 wr := &watchdogReader{ 122 reader: bytes.NewReader(byteString), 123 chunkSize: 3, //len(byteString) % chunkSize != 0 to be extra rude :) 124 //don't care about the timeout stuff here 125 timeout: 5 * time.Minute, 126 timer: time.NewTimer(5 * time.Minute), 127 } 128 129 b := make([]byte, len(byteString)) 130 n, err := io.ReadFull(wr, b) 131 if err != nil || n != len(b) { 132 t.Fatalf("Read error: %s", err) 133 } 134 if !bytes.Equal(b, byteString) { 135 t.Fatalf("Bad read: %#v != %#v", string(b), string(byteString)) 136 } 137} 138