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