github.com/ericwq/aprilsh@v0.0.0-20240517091432-958bc568daa0/frontend/read_test.go (about) 1 // Copyright 2022~2023 wangqi. All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE file. 4 5 package frontend 6 7 import ( 8 "errors" 9 "io" 10 "net" 11 "os" 12 "sync" 13 "testing" 14 "time" 15 ) 16 17 type mockFile struct { 18 round int 19 timeout []int 20 data []string 21 err []error 22 limit int 23 } 24 25 func (m *mockFile) Read(p []byte) (n int, err error) { 26 if m.round >= 0 && m.round < len(m.data) { 27 28 // make sure we increase round 29 defer func() { m.round++ }() 30 31 // support read timeout 32 time.Sleep(time.Duration(m.timeout[m.round]) * time.Millisecond) 33 if m.timeout[m.round] > m.limit { 34 err = os.ErrDeadlineExceeded 35 // fmt.Printf("#mockFile Read round=%d, n=%d, err=%s\n", m.round, n, err) 36 return 37 } else if m.timeout[m.round] == m.limit { 38 err = os.ErrPermission 39 // fmt.Printf("#mockFile Read round=%d, n=%d, err=%s\n", m.round, n, err) 40 return 41 } 42 43 // normal read 44 copy(p, []byte(m.data[m.round])) 45 n = len(m.data[m.round]) 46 err = nil 47 // fmt.Printf("#mockFile Read round=%d, n=%d, err=%s\n", m.round, n, err) 48 return 49 } 50 m.round = 0 51 n = 0 52 err = io.EOF 53 // fmt.Printf("#mockFile Read round=%d, n=%d, err=%s\n", m.round, n, err) 54 return 55 } 56 57 func TestReadFromFile(t *testing.T) { 58 // prepare the data 59 mockReader := &mockFile{} 60 mockReader.round = 0 61 mockReader.limit = 10 62 mockReader.timeout = []int{5, 5, 7, 3, 8, 15} 63 mockReader.data = []string{"zero", "one", "two", "tree", "four", "five"} 64 mockReader.err = []error{nil, nil, nil, nil, nil, os.ErrDeadlineExceeded} 65 66 var fileChan chan Message 67 var doneChan chan any 68 fileChan = make(chan Message, 0) 69 doneChan = make(chan any, 1) 70 71 // start the deal line reader 72 var wg sync.WaitGroup 73 wg.Add(1) 74 go func() { 75 defer wg.Done() 76 ReadFromFile(mockReader.limit, fileChan, doneChan, mockReader) 77 }() 78 79 // check the consistency of mock data 80 if len(mockReader.data) != len(mockReader.err) || len(mockReader.err) != len(mockReader.timeout) { 81 t.Errorf("#test ReadFromFile the size of data is not equeal. %d,%d,%d \n", 82 len(mockReader.timeout), len(mockReader.data), len(mockReader.err)) 83 } 84 85 // consume the data from reader 86 for i := range mockReader.err { 87 // got message from reader channel 88 fileMsg := <-fileChan 89 // fmt.Printf("got %s,%s\n", fileMsg.Data, fileMsg.Err) 90 if mockReader.err[i] != nil { 91 if !errors.Is(fileMsg.Err, mockReader.err[i]) { 92 t.Errorf("#test ReadFromFile expect %s, got %s\n", mockReader.err[i], fileMsg.Err) 93 } 94 if "" != fileMsg.Data { 95 t.Errorf("#test ReadFromFile expect %q, got %s\n", "", fileMsg.Data) 96 } 97 } else { 98 if mockReader.data[i] != fileMsg.Data { 99 t.Errorf("#test ReadFromFile expect %s, got %s\n", mockReader.data[i], fileMsg.Data) 100 } 101 } 102 } 103 104 doneChan <- "done" 105 // consume EOF message 106 select { 107 case <-fileChan: 108 default: 109 } 110 wg.Wait() 111 } 112 113 func TestReadFromFile_DoneChan(t *testing.T) { 114 // prepare the data 115 mockReader := &mockFile{} 116 mockReader.round = 0 117 mockReader.limit = 10 118 mockReader.timeout = []int{5, 5, 7, 3, 8, 10} 119 mockReader.data = []string{"zero+", "one+", "two+", "tree+", "four+", "five+"} 120 mockReader.err = []error{nil, nil, nil, nil, nil, os.ErrPermission} 121 122 var fileChan chan Message 123 var doneChan chan any 124 fileChan = make(chan Message, 1) 125 doneChan = make(chan any, 1) 126 127 // start the deal line reader 128 var wg sync.WaitGroup 129 wg.Add(1) 130 go func() { 131 defer wg.Done() 132 ReadFromFile(mockReader.limit, fileChan, doneChan, mockReader) 133 }() 134 135 // check the consistency of mock data 136 if len(mockReader.data) != len(mockReader.err) || len(mockReader.err) != len(mockReader.timeout) { 137 t.Errorf("#test ReadFromFile the size of data is not equeal. %d,%d,%d \n", 138 len(mockReader.timeout), len(mockReader.data), len(mockReader.err)) 139 } 140 141 // consume the data from reader 142 for i := range mockReader.data { 143 // got message from reader channel 144 fileMsg := <-fileChan 145 if mockReader.err[i] != nil { 146 if !errors.Is(fileMsg.Err, mockReader.err[i]) { 147 t.Errorf("#test ReadFromFile expect %s, got %s\n", mockReader.err[i], fileMsg.Err) 148 } 149 if "" != fileMsg.Data { 150 t.Errorf("#test ReadFromFile expect %q, got %s\n", "", fileMsg.Data) 151 } 152 } else { 153 if mockReader.data[i] != fileMsg.Data { 154 t.Errorf("#test ReadFromFile expect %s, got %s\n", mockReader.data[i], fileMsg.Data) 155 } 156 } 157 158 // early shutdown 159 if i == 2 { 160 doneChan <- "done" 161 break 162 } 163 } 164 165 // consume last message to release the reader 166 select { 167 case <-fileChan: 168 default: 169 } 170 wg.Wait() 171 } 172 173 type mockConnection struct { 174 round int 175 timeout []int 176 data []string 177 err []error 178 limit int 179 } 180 181 // func (m *mockDeadLineReceiver) SetReadDeadline(t time.Time) error { 182 // return nil 183 // } 184 185 func (m *mockConnection) Recv(timeout int) (payload string, rAddr net.Addr, err error) { 186 // func (m *mockDeadLineReceiver) Recv() (err error) { 187 if m.round >= 0 && m.round < len(m.err) { 188 // make sure we increase round 189 defer func() { m.round++ }() 190 191 // support read timeout 192 time.Sleep(time.Duration(m.timeout[m.round]) * time.Millisecond) 193 if m.timeout[m.round] > m.limit { 194 err = os.ErrDeadlineExceeded 195 // fmt.Printf("#mockConnection Read round=%d, data=%s, err=%s\n", m.round, payload, err) 196 return 197 } else if m.timeout[m.round] == m.limit { 198 err = os.ErrPermission 199 // fmt.Printf("#mockConnection Read round=%d, data=%s, err=%s\n", m.round, payload, err) 200 return 201 } 202 // normal read 203 payload = m.data[m.round] 204 err = nil 205 // fmt.Printf("#mockConnection Read round=%d, data=%s, err=%s\n", m.round, payload, err) 206 return 207 } 208 209 m.round = 0 210 // err = net.ErrClosed 211 err = net.ErrClosed 212 // fmt.Printf("#mockConnection* Read round=%d, data=%s, err=%s\n", m.round, payload, err) 213 return 214 } 215 216 func TestReadFromNetwork(t *testing.T) { 217 // prepare the data 218 mr := &mockConnection{} 219 mr.round = 0 220 mr.limit = 10 221 mr.timeout = []int{5, 15, 7, 3, 8, 10} 222 mr.data = []string{"zero>", "one>", "two>", "tree>", "four>", "five>"} 223 mr.err = []error{nil, os.ErrDeadlineExceeded, nil, nil, nil, os.ErrPermission} 224 225 var networkChan chan Message 226 var doneChan chan any 227 networkChan = make(chan Message, 1) 228 doneChan = make(chan any, 1) 229 230 // start the deal line reader 231 var wg sync.WaitGroup 232 wg.Add(1) 233 go func() { 234 defer wg.Done() 235 ReadFromNetwork(mr.limit, networkChan, doneChan, mr) 236 }() 237 238 // check the consistency of mock data 239 if len(mr.err) != len(mr.timeout) { 240 t.Errorf("#test ReadFromNetwork the size of err and timeout is not equeal. %d,%d \n", 241 len(mr.timeout), len(mr.err)) 242 } 243 244 // consume the data from reader 245 for i := range mr.err { 246 if mr.err[i] == os.ErrDeadlineExceeded { 247 continue 248 } 249 250 // got message from reader channel 251 netMsg := <-networkChan 252 // fmt.Printf("#TestReadFromNetwork got %s,%s\n", netMsg.Data, netMsg.Err) 253 254 if mr.err[i] != nil { 255 if !errors.Is(netMsg.Err, mr.err[i]) { 256 t.Errorf("#test ReadFromFile expect %s, got %s\n", mr.err[i], netMsg.Err) 257 } 258 if "" != netMsg.Data { 259 t.Errorf("#test ReadFromFile expect %q, got %s\n", "", netMsg.Data) 260 } 261 } else { 262 if mr.data[i] != netMsg.Data { 263 t.Errorf("#test ReadFromFile expect %s, got %s\n", mr.data[i], netMsg.Data) 264 } 265 } 266 } 267 268 // before the last read operation, we send shutdown message to the reader 269 doneChan <- "done" 270 271 // consume last message to release the reader 272 // select { 273 // case <-networkChan: 274 // default: 275 // } 276 wg.Wait() 277 } 278 279 func TestReadFromNetwork_ErrClosed(t *testing.T) { 280 // prepare the data 281 mr := &mockConnection{} 282 mr.round = 0 283 mr.limit = 10 284 mr.timeout = []int{5, 5, 7, 3, 8, 10} 285 mr.data = []string{"zero*", "one*", "two*", "tree*", "four*", "five*"} 286 mr.err = []error{nil, nil, nil, nil, nil, os.ErrPermission} 287 288 var networkChan chan Message 289 var doneChan chan any 290 networkChan = make(chan Message, 1) 291 doneChan = make(chan any, 1) 292 293 // start the deal line reader 294 var wg sync.WaitGroup 295 wg.Add(1) 296 go func() { 297 defer wg.Done() 298 ReadFromNetwork(mr.limit, networkChan, doneChan, mr) 299 }() 300 301 // check the consistency of mock data 302 if len(mr.err) != len(mr.timeout) { 303 t.Errorf("#test ReadFromNetwork the size of err and timeout is not equeal. %d,%d \n", 304 len(mr.timeout), len(mr.err)) 305 } 306 307 // consume the data from reader 308 for i := range mr.err { 309 if mr.err[i] == os.ErrDeadlineExceeded { 310 continue 311 } 312 313 // got message from reader channel 314 netMsg := <-networkChan 315 316 // fmt.Printf("got %s,%s\n", netMsg.Data, netMsg.Err) 317 if mr.err[i] != nil { 318 if !errors.Is(netMsg.Err, mr.err[i]) { 319 t.Errorf("#test ReadFromFile expect %s, got %s\n", mr.err[i], netMsg.Err) 320 } 321 if "" != netMsg.Data { 322 t.Errorf("#test ReadFromFile expect %q, got %s\n", "", netMsg.Data) 323 } 324 } else { 325 if mr.data[i] != netMsg.Data { 326 t.Errorf("#test ReadFromFile expect %s, got %s\n", mr.data[i], netMsg.Data) 327 } 328 } 329 } 330 331 // the last read will get net.ErrClosed error, which will stop the ReadFromNetwork goroutine. 332 wg.Wait() 333 }