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