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  }