github.com/evdatsion/aphelion-dpos-bft@v0.32.1/libs/flowrate/io_test.go (about) 1 // 2 // Written by Maxim Khitrov (November 2012) 3 // 4 5 package flowrate 6 7 import ( 8 "bytes" 9 "testing" 10 "time" 11 ) 12 13 const ( 14 _50ms = 50 * time.Millisecond 15 _100ms = 100 * time.Millisecond 16 _200ms = 200 * time.Millisecond 17 _300ms = 300 * time.Millisecond 18 _400ms = 400 * time.Millisecond 19 _500ms = 500 * time.Millisecond 20 ) 21 22 func nextStatus(m *Monitor) Status { 23 samples := m.samples 24 for i := 0; i < 30; i++ { 25 if s := m.Status(); s.Samples != samples { 26 return s 27 } 28 time.Sleep(5 * time.Millisecond) 29 } 30 return m.Status() 31 } 32 33 func TestReader(t *testing.T) { 34 in := make([]byte, 100) 35 for i := range in { 36 in[i] = byte(i) 37 } 38 b := make([]byte, 100) 39 r := NewReader(bytes.NewReader(in), 100) 40 start := time.Now() 41 42 // Make sure r implements Limiter 43 _ = Limiter(r) 44 45 // 1st read of 10 bytes is performed immediately 46 if n, err := r.Read(b); n != 10 || err != nil { 47 t.Fatalf("r.Read(b) expected 10 (<nil>); got %v (%v)", n, err) 48 } else if rt := time.Since(start); rt > _50ms { 49 t.Fatalf("r.Read(b) took too long (%v)", rt) 50 } 51 52 // No new Reads allowed in the current sample 53 r.SetBlocking(false) 54 if n, err := r.Read(b); n != 0 || err != nil { 55 t.Fatalf("r.Read(b) expected 0 (<nil>); got %v (%v)", n, err) 56 } else if rt := time.Since(start); rt > _50ms { 57 t.Fatalf("r.Read(b) took too long (%v)", rt) 58 } 59 60 status := [6]Status{0: r.Status()} // No samples in the first status 61 62 // 2nd read of 10 bytes blocks until the next sample 63 r.SetBlocking(true) 64 if n, err := r.Read(b[10:]); n != 10 || err != nil { 65 t.Fatalf("r.Read(b[10:]) expected 10 (<nil>); got %v (%v)", n, err) 66 } else if rt := time.Since(start); rt < _100ms { 67 t.Fatalf("r.Read(b[10:]) returned ahead of time (%v)", rt) 68 } 69 70 status[1] = r.Status() // 1st sample 71 status[2] = nextStatus(r.Monitor) // 2nd sample 72 status[3] = nextStatus(r.Monitor) // No activity for the 3rd sample 73 74 if n := r.Done(); n != 20 { 75 t.Fatalf("r.Done() expected 20; got %v", n) 76 } 77 78 status[4] = r.Status() 79 status[5] = nextStatus(r.Monitor) // Timeout 80 start = status[0].Start 81 82 // Active, Start, Duration, Idle, Bytes, Samples, InstRate, CurRate, AvgRate, PeakRate, BytesRem, TimeRem, Progress 83 want := []Status{ 84 {true, start, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 85 {true, start, _100ms, 0, 10, 1, 100, 100, 100, 100, 0, 0, 0}, 86 {true, start, _200ms, _100ms, 20, 2, 100, 100, 100, 100, 0, 0, 0}, 87 {true, start, _300ms, _200ms, 20, 3, 0, 90, 67, 100, 0, 0, 0}, 88 {false, start, _300ms, 0, 20, 3, 0, 0, 67, 100, 0, 0, 0}, 89 {false, start, _300ms, 0, 20, 3, 0, 0, 67, 100, 0, 0, 0}, 90 } 91 for i, s := range status { 92 if !statusesAreEqual(&s, &want[i]) { 93 t.Errorf("r.Status(%v)\nexpected: %v\ngot : %v", i, want[i], s) 94 } 95 } 96 if !bytes.Equal(b[:20], in[:20]) { 97 t.Errorf("r.Read() input doesn't match output") 98 } 99 } 100 101 func TestWriter(t *testing.T) { 102 b := make([]byte, 100) 103 for i := range b { 104 b[i] = byte(i) 105 } 106 w := NewWriter(&bytes.Buffer{}, 200) 107 start := time.Now() 108 109 // Make sure w implements Limiter 110 _ = Limiter(w) 111 112 // Non-blocking 20-byte write for the first sample returns ErrLimit 113 w.SetBlocking(false) 114 if n, err := w.Write(b); n != 20 || err != ErrLimit { 115 t.Fatalf("w.Write(b) expected 20 (ErrLimit); got %v (%v)", n, err) 116 } else if rt := time.Since(start); rt > _50ms { 117 t.Fatalf("w.Write(b) took too long (%v)", rt) 118 } 119 120 // Blocking 80-byte write 121 w.SetBlocking(true) 122 if n, err := w.Write(b[20:]); n != 80 || err != nil { 123 t.Fatalf("w.Write(b[20:]) expected 80 (<nil>); got %v (%v)", n, err) 124 } else if rt := time.Since(start); rt < _300ms { 125 // Explanation for `rt < _300ms` (as opposed to `< _400ms`) 126 // 127 // |<-- start | | 128 // epochs: -----0ms|---100ms|---200ms|---300ms|---400ms 129 // sends: 20|20 |20 |20 |20# 130 // 131 // NOTE: The '#' symbol can thus happen before 400ms is up. 132 // Thus, we can only panic if rt < _300ms. 133 t.Fatalf("w.Write(b[20:]) returned ahead of time (%v)", rt) 134 } 135 136 w.SetTransferSize(100) 137 status := []Status{w.Status(), nextStatus(w.Monitor)} 138 start = status[0].Start 139 140 // Active, Start, Duration, Idle, Bytes, Samples, InstRate, CurRate, AvgRate, PeakRate, BytesRem, TimeRem, Progress 141 want := []Status{ 142 {true, start, _400ms, 0, 80, 4, 200, 200, 200, 200, 20, _100ms, 80000}, 143 {true, start, _500ms, _100ms, 100, 5, 200, 200, 200, 200, 0, 0, 100000}, 144 } 145 for i, s := range status { 146 if !statusesAreEqual(&s, &want[i]) { 147 t.Errorf("w.Status(%v)\nexpected: %v\ngot : %v\n", i, want[i], s) 148 } 149 } 150 if !bytes.Equal(b, w.Writer.(*bytes.Buffer).Bytes()) { 151 t.Errorf("w.Write() input doesn't match output") 152 } 153 } 154 155 const maxDeviationForDuration = 50 * time.Millisecond 156 const maxDeviationForRate int64 = 50 157 158 // statusesAreEqual returns true if s1 is equal to s2. Equality here means 159 // general equality of fields except for the duration and rates, which can 160 // drift due to unpredictable delays (e.g. thread wakes up 25ms after 161 // `time.Sleep` has ended). 162 func statusesAreEqual(s1 *Status, s2 *Status) bool { 163 if s1.Active == s2.Active && 164 s1.Start == s2.Start && 165 durationsAreEqual(s1.Duration, s2.Duration, maxDeviationForDuration) && 166 s1.Idle == s2.Idle && 167 s1.Bytes == s2.Bytes && 168 s1.Samples == s2.Samples && 169 ratesAreEqual(s1.InstRate, s2.InstRate, maxDeviationForRate) && 170 ratesAreEqual(s1.CurRate, s2.CurRate, maxDeviationForRate) && 171 ratesAreEqual(s1.AvgRate, s2.AvgRate, maxDeviationForRate) && 172 ratesAreEqual(s1.PeakRate, s2.PeakRate, maxDeviationForRate) && 173 s1.BytesRem == s2.BytesRem && 174 durationsAreEqual(s1.TimeRem, s2.TimeRem, maxDeviationForDuration) && 175 s1.Progress == s2.Progress { 176 return true 177 } 178 return false 179 } 180 181 func durationsAreEqual(d1 time.Duration, d2 time.Duration, maxDeviation time.Duration) bool { 182 return d2-d1 <= maxDeviation 183 } 184 185 func ratesAreEqual(r1 int64, r2 int64, maxDeviation int64) bool { 186 sub := r1 - r2 187 if sub < 0 { 188 sub = -sub 189 } 190 if sub <= maxDeviation { 191 return true 192 } 193 return false 194 }