/* NAME ring_test.go - a test suite adopting the golang testing library to test functionality of the RingBuffer structure DESCRIPTION See README.md AUTHOR Dan Kortschak LICENSE ring_test.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. */ package ring import ( "io" "reflect" "strings" "sync" "testing" "time" ) var roundTripTests = []struct { name string len int size int timeout time.Duration nextTimeout time.Duration data [][]string readDelay time.Duration writeDelay time.Duration }{ { name: "happy", len: 2, size: 50, timeout: 100 * time.Millisecond, nextTimeout: 100 * time.Millisecond, data: [][]string{ {"frame1", "frame2", "frame3", "frame4"}, {"frame5", "frame6"}, {"frame5", "frame6", "frame7"}, {"frame8", "frame9", "frame10"}, {"frame11"}, {"frame12", "frame13"}, {"frame14", "frame15", "frame16", "frame17"}, }, }, { name: "slow write", len: 2, size: 50, timeout: 100 * time.Millisecond, nextTimeout: 100 * time.Millisecond, data: [][]string{ {"frame1", "frame2", "frame3", "frame4"}, {"frame5", "frame6"}, {"frame5", "frame6", "frame7"}, {"frame8", "frame9", "frame10"}, {"frame11"}, {"frame12", "frame13"}, {"frame14", "frame15", "frame16", "frame17"}, }, writeDelay: 500 * time.Millisecond, }, { name: "slow read", len: 2, size: 50, timeout: 100 * time.Millisecond, nextTimeout: 100 * time.Millisecond, data: [][]string{ {"frame1", "frame2", "frame3", "frame4"}, {"frame5", "frame6"}, {"frame5", "frame6", "frame7"}, {"frame8", "frame9", "frame10"}, {"frame11"}, {"frame12", "frame13"}, {"frame14", "frame15", "frame16", "frame17"}, }, readDelay: 500 * time.Millisecond, }, } func TestRoundTrip(t *testing.T) { const maxTimeouts = 100 for _, test := range roundTripTests { b := NewBuffer(test.len, test.size, test.timeout) var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() for _, c := range test.data { var timeouts int for _, f := range c { time.Sleep(test.writeDelay) // Simulate slow data capture. _, err := b.Write([]byte(f)) switch err { case nil: timeouts = 0 case ErrTimeout: if timeouts > maxTimeouts { t.Errorf("too many write timeouts for %q", test.name) return } timeouts++ default: t.Errorf("unexpected write error for %q: %v", test.name, err) return } } b.Flush() } b.Close() }() go func() { buf := make([]byte, 1<<10) defer wg.Done() var got []string var timeouts int elements: for { err := b.Next(test.nextTimeout) switch err { case nil: timeouts = 0 case ErrTimeout: if timeouts > maxTimeouts { t.Errorf("too many timeouts for %q", test.name) return } timeouts++ case io.EOF: break elements default: t.Errorf("unexpected read error for %q: %v", test.name, err) return } reads: for { n, err := b.Read(buf) if n != 0 { time.Sleep(test.readDelay) // Simulate slow data processing. got = append(got, string(buf[:n])) } switch err { case nil: case io.EOF: break reads default: t.Errorf("unexpected read error for %q: %v", test.name, err) return } } } var want []string for _, c := range test.data { want = append(want, strings.Join(c, "")) } if test.readDelay == 0 { if !reflect.DeepEqual(got, want) { t.Errorf("unexpected round-trip result for %q:\ngot: %#v\nwant:%#v", test.name, got, want) } } else { // We may have dropped writes in this case. // So just check that we can consume every // received element with reference to what // was sent. // TODO(kortschak): Check that the number of // missing elements matches the number of // dropped writes. var sidx, ridx int var recd string for ridx, recd = range got { for ; sidx < len(want); sidx++ { if recd == want[sidx] { break } } } if ridx != len(got)-1 { t.Errorf("unexpected round-trip result for %q (unexplained element received):\ngot: %#v\nwant:%#v", test.name, got, want) } } }() wg.Wait() } }