// // Written by Maxim Khitrov (November 2012) // package flowrate import ( "bytes" "testing" "time" ) const ( _50ms = 50 * time.Millisecond _100ms = 100 * time.Millisecond _200ms = 200 * time.Millisecond _300ms = 300 * time.Millisecond _400ms = 400 * time.Millisecond _500ms = 500 * time.Millisecond ) func nextStatus(m *Monitor) Status { samples := m.samples for i := 0; i < 30; i++ { if s := m.Status(); s.Samples != samples { return s } time.Sleep(5 * time.Millisecond) } return m.Status() } func TestReader(t *testing.T) { in := make([]byte, 100) for i := range in { in[i] = byte(i) } b := make([]byte, 100) r := NewReader(bytes.NewReader(in), 100) start := time.Now() // Make sure r implements Limiter _ = Limiter(r) // 1st read of 10 bytes is performed immediately if n, err := r.Read(b); n != 10 || err != nil { t.Fatalf("r.Read(b) expected 10 (); got %v (%v)", n, err) } else if rt := time.Since(start); rt > _50ms { t.Fatalf("r.Read(b) took too long (%v)", rt) } // No new Reads allowed in the current sample r.SetBlocking(false) if n, err := r.Read(b); n != 0 || err != nil { t.Fatalf("r.Read(b) expected 0 (); got %v (%v)", n, err) } else if rt := time.Since(start); rt > _50ms { t.Fatalf("r.Read(b) took too long (%v)", rt) } status := [6]Status{0: r.Status()} // No samples in the first status // 2nd read of 10 bytes blocks until the next sample r.SetBlocking(true) if n, err := r.Read(b[10:]); n != 10 || err != nil { t.Fatalf("r.Read(b[10:]) expected 10 (); got %v (%v)", n, err) } else if rt := time.Since(start); rt < _100ms { t.Fatalf("r.Read(b[10:]) returned ahead of time (%v)", rt) } status[1] = r.Status() // 1st sample status[2] = nextStatus(r.Monitor) // 2nd sample status[3] = nextStatus(r.Monitor) // No activity for the 3rd sample if n := r.Done(); n != 20 { t.Fatalf("r.Done() expected 20; got %v", n) } status[4] = r.Status() status[5] = nextStatus(r.Monitor) // Timeout start = status[0].Start // Active, Bytes, Samples, InstRate, CurRate, AvgRate, PeakRate, BytesRem, Start, Duration, Idle, TimeRem, Progress want := []Status{ {true, 0, 0, 0, 0, 0, 0, 0, start, 0, 0, 0, 0}, {true, 10, 1, 100, 100, 100, 100, 0, start, _100ms, 0, 0, 0}, {true, 20, 2, 100, 100, 100, 100, 0, start, _200ms, _100ms, 0, 0}, {true, 20, 3, 0, 90, 67, 100, 0, start, _300ms, _200ms, 0, 0}, {false, 20, 3, 0, 0, 67, 100, 0, start, _300ms, 0, 0, 0}, {false, 20, 3, 0, 0, 67, 100, 0, start, _300ms, 0, 0, 0}, } for i, s := range status { s := s if !statusesAreEqual(&s, &want[i]) { t.Errorf("r.Status(%v)\nexpected: %v\ngot : %v", i, want[i], s) } } if !bytes.Equal(b[:20], in[:20]) { t.Errorf("r.Read() input doesn't match output") } } func TestWriter(t *testing.T) { b := make([]byte, 100) for i := range b { b[i] = byte(i) } w := NewWriter(&bytes.Buffer{}, 200) start := time.Now() // Make sure w implements Limiter _ = Limiter(w) // Non-blocking 20-byte write for the first sample returns ErrLimit w.SetBlocking(false) if n, err := w.Write(b); n != 20 || err != ErrLimit { t.Fatalf("w.Write(b) expected 20 (ErrLimit); got %v (%v)", n, err) } else if rt := time.Since(start); rt > _50ms { t.Fatalf("w.Write(b) took too long (%v)", rt) } // Blocking 80-byte write w.SetBlocking(true) if n, err := w.Write(b[20:]); n != 80 || err != nil { t.Fatalf("w.Write(b[20:]) expected 80 (); got %v (%v)", n, err) } else if rt := time.Since(start); rt < _300ms { // Explanation for `rt < _300ms` (as opposed to `< _400ms`) // // |<-- start | | // epochs: -----0ms|---100ms|---200ms|---300ms|---400ms // sends: 20|20 |20 |20 |20# // // NOTE: The '#' symbol can thus happen before 400ms is up. // Thus, we can only panic if rt < _300ms. t.Fatalf("w.Write(b[20:]) returned ahead of time (%v)", rt) } w.SetTransferSize(100) status := []Status{w.Status(), nextStatus(w.Monitor)} start = status[0].Start // Active, Bytes, Samples, InstRate, CurRate, AvgRate, PeakRate, BytesRem, Start, Duration, Idle, TimeRem, Progress want := []Status{ {true, 80, 4, 200, 200, 200, 200, 20, start, _400ms, 0, _100ms, 80000}, {true, 100, 5, 200, 200, 200, 200, 0, start, _500ms, _100ms, 0, 100000}, } for i, s := range status { s := s if !statusesAreEqual(&s, &want[i]) { t.Errorf("w.Status(%v)\nexpected: %v\ngot : %v\n", i, want[i], s) } } if !bytes.Equal(b, w.Writer.(*bytes.Buffer).Bytes()) { t.Errorf("w.Write() input doesn't match output") } } const maxDeviationForDuration = 50 * time.Millisecond const maxDeviationForRate int64 = 50 // statusesAreEqual returns true if s1 is equal to s2. Equality here means // general equality of fields except for the duration and rates, which can // drift due to unpredictable delays (e.g. thread wakes up 25ms after // `time.Sleep` has ended). func statusesAreEqual(s1 *Status, s2 *Status) bool { if s1.Active == s2.Active && s1.Start == s2.Start && durationsAreEqual(s1.Duration, s2.Duration, maxDeviationForDuration) && s1.Idle == s2.Idle && s1.Bytes == s2.Bytes && s1.Samples == s2.Samples && ratesAreEqual(s1.InstRate, s2.InstRate, maxDeviationForRate) && ratesAreEqual(s1.CurRate, s2.CurRate, maxDeviationForRate) && ratesAreEqual(s1.AvgRate, s2.AvgRate, maxDeviationForRate) && ratesAreEqual(s1.PeakRate, s2.PeakRate, maxDeviationForRate) && s1.BytesRem == s2.BytesRem && durationsAreEqual(s1.TimeRem, s2.TimeRem, maxDeviationForDuration) && s1.Progress == s2.Progress { return true } return false } func durationsAreEqual(d1 time.Duration, d2 time.Duration, maxDeviation time.Duration) bool { return d2-d1 <= maxDeviation } func ratesAreEqual(r1 int64, r2 int64, maxDeviation int64) bool { sub := r1 - r2 if sub < 0 { sub = -sub } if sub <= maxDeviation { return true } return false }