| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package iomisc |
| |
| import ( |
| "bytes" |
| "context" |
| "errors" |
| "io" |
| "testing" |
| "time" |
| ) |
| |
| func TestMatchingReader(t *testing.T) { |
| t.Run("sequence appears in a single read", func(t *testing.T) { |
| sequence := []byte("ABCDE") |
| var buf bytes.Buffer |
| m := NewMatchingReader(&buf, [][]byte{sequence}) |
| assertMatch(t, m, nil) |
| |
| buf.Write(sequence) |
| p := make([]byte, 1024) |
| if _, err := m.Read(p); err != nil && !errors.Is(err, io.EOF) { |
| t.Fatalf("unexpected error: %v", err) |
| } |
| assertMatch(t, m, sequence) |
| }) |
| |
| t.Run("sequence appears across multiple reads", func(t *testing.T) { |
| sequence := []byte("ABCDE") |
| var buf bytes.Buffer |
| m := NewMatchingReader(&buf, [][]byte{sequence}) |
| assertMatch(t, m, nil) |
| |
| buf.Write([]byte("ABC")) |
| p := make([]byte, 1024) |
| if _, err := m.Read(p); err != nil { |
| t.Fatalf("unexpected error: %v", err) |
| } |
| assertMatch(t, m, nil) |
| |
| buf.Write([]byte("D")) |
| p = make([]byte, 1024) |
| if _, err := m.Read(p); err != nil { |
| t.Fatalf("unexpected error: %v", err) |
| } |
| assertMatch(t, m, nil) |
| |
| buf.Write([]byte("EFGH")) |
| p = make([]byte, 1024) |
| if _, err := m.Read(p); err != nil && !errors.Is(err, io.EOF) { |
| t.Fatalf("unexpected error: %v", err) |
| } |
| assertMatch(t, m, sequence) |
| }) |
| |
| t.Run("Read throws EOFs after match", func(t *testing.T) { |
| sequence := []byte("ABCDE") |
| var buf bytes.Buffer |
| m := NewMatchingReader(&buf, [][]byte{sequence}) |
| assertMatch(t, m, nil) |
| |
| buf.Write([]byte("ABCDE")) |
| p := make([]byte, 1024) |
| if _, err := m.Read(p); err != nil && !errors.Is(err, io.EOF) { |
| t.Fatalf("unexpected error: %v", err) |
| } |
| assertMatch(t, m, sequence) |
| |
| buf.Write([]byte("FGHIJK")) |
| p = make([]byte, 1024) |
| if _, err := m.Read(p); !errors.Is(err, io.EOF) { |
| t.Fatalf("unexpected error: expected EOF; got %v", err) |
| } |
| assertMatch(t, m, sequence) |
| }) |
| |
| t.Run("multiple sequences", func(t *testing.T) { |
| sequences := [][]byte{[]byte("ABCDE"), []byte("BCDEF")} |
| var buf bytes.Buffer |
| m := NewMatchingReader(&buf, sequences) |
| assertMatch(t, m, nil) |
| |
| buf.Write([]byte("BCDE")) |
| p := make([]byte, 1024) |
| if _, err := m.Read(p); err != nil { |
| t.Fatalf("unexpected error: %v", err) |
| } |
| assertMatch(t, m, nil) |
| |
| buf.Write([]byte("FGHIJK")) |
| p = make([]byte, 1024) |
| if _, err := m.Read(p); err != nil && !errors.Is(err, io.EOF) { |
| t.Fatalf("unexpected error: %v", err) |
| } |
| assertMatch(t, m, sequences[1]) |
| }) |
| |
| t.Run("sequence appears mid-read", func(t *testing.T) { |
| sequence := []byte("BC") |
| var buf bytes.Buffer |
| m := NewMatchingReader(&buf, [][]byte{sequence}) |
| assertMatch(t, m, nil) |
| buf.Write([]byte("ABCD")) |
| p := make([]byte, 1024) |
| if _, err := m.Read(p); err != nil && !errors.Is(err, io.EOF) { |
| t.Fatalf("unexpected error: %v", err) |
| } |
| assertMatch(t, m, sequence) |
| }) |
| } |
| |
| func assertMatch(t *testing.T, m *MatchingReader, match []byte) { |
| t.Helper() |
| if bytes.Compare(match, m.Match()) != 0 { |
| t.Fatalf("expected match of %q; not %q", match, m.Match()) |
| } |
| } |
| |
| func TestReadUntilMatch(t *testing.T) { |
| t.Run("success", func(t *testing.T) { |
| r, w := io.Pipe() |
| defer w.Close() |
| defer r.Close() |
| |
| m := NewMatchingReader(r, [][]byte{[]byte("ABCDE")}) |
| |
| go func() { |
| w.Write([]byte("ABC")) |
| w.Write([]byte("D")) |
| w.Write([]byte("EFGH")) |
| }() |
| |
| match, err := ReadUntilMatch(context.Background(), m) |
| |
| if err != nil { |
| t.Fatalf("unexpected error: %v", err) |
| } |
| if bytes.Compare(match, []byte("ABCDE")) != 0 { |
| t.Fatalf("expected match of ABCDE; not %q", match) |
| } |
| }) |
| |
| t.Run("read fails", func(t *testing.T) { |
| r, w := io.Pipe() |
| defer r.Close() |
| |
| m := NewMatchingReader(r, [][]byte{[]byte("foo")}) |
| |
| go func() { |
| w.Write([]byte("bar")) |
| w.Close() |
| }() |
| |
| _, err := ReadUntilMatch(context.Background(), m) |
| |
| if !errors.Is(err, io.EOF) { |
| t.Errorf("ReadUntilMatch() returned %v, want io.EOF", err) |
| } |
| }) |
| |
| t.Run("cancellation", func(t *testing.T) { |
| r, w := io.Pipe() |
| defer r.Close() |
| defer w.Close() |
| |
| m := NewMatchingReader(r, [][]byte{[]byte("A")}) |
| |
| ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond)) |
| defer cancel() |
| |
| go func() { |
| b := []byte("B") |
| for { |
| w.Write(b) |
| } |
| }() |
| |
| if _, err := ReadUntilMatch(ctx, m); err == nil || !errors.Is(err, context.DeadlineExceeded) { |
| t.Errorf("ReadUntilMatch() returned %v, want DeadlineExceeded ", err) |
| } |
| }) |
| } |