blob: f94a2076c285b6242972eaba5b2648892fba75c9 [file] [log] [blame]
// 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"
"regexp"
"testing"
)
func TestMatchingReader(t *testing.T) {
t.Run("sequence appears in a single read", func(t *testing.T) {
sequence := "ABCDE"
var buf bytes.Buffer
m := NewSequenceMatchingReader(&buf, sequence)
assertMatch(t, m, nil)
buf.Write([]byte(sequence))
p := make([]byte, 1024)
if _, err := m.Read(p); err != nil {
t.Fatalf("unexpected error: %v", err)
}
assertMatch(t, m, []byte(sequence))
})
t.Run("sequence appears across multiple reads", func(t *testing.T) {
sequence := "ABCDE"
var buf bytes.Buffer
m := NewSequenceMatchingReader(&buf, 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 {
t.Fatalf("unexpected error: %v", err)
}
assertMatch(t, m, []byte(sequence))
})
t.Run("pattern appears in a single read", func(t *testing.T) {
pattern := "a[0-9]{1,2}[B-D]"
re := regexp.MustCompile(pattern)
var buf bytes.Buffer
m := NewPatternMatchingReader(&buf, re, 4)
assertMatch(t, m, nil)
buf.Write([]byte("a03C"))
p := make([]byte, 1024)
if _, err := m.Read(p); err != nil {
t.Fatalf("unexpected error: %v", err)
}
assertMatch(t, m, []byte("a03C"))
})
t.Run("pattern appears across multiple reads", func(t *testing.T) {
pattern := "a[0-9]{1,2}[B-D]"
re := regexp.MustCompile(pattern)
var buf bytes.Buffer
m := NewPatternMatchingReader(&buf, re, 4)
assertMatch(t, m, nil)
buf.Write([]byte("12345a"))
p := make([]byte, 1024)
if _, err := m.Read(p); err != nil {
t.Fatalf("unexpected error: %v", err)
}
assertMatch(t, m, []byte(nil))
buf.Write([]byte("03"))
p = make([]byte, 1024)
if _, err := m.Read(p); err != nil {
t.Fatalf("unexpected error: %v", err)
}
assertMatch(t, m, []byte(nil))
buf.Write([]byte("C"))
p = make([]byte, 1024)
if _, err := m.Read(p); err != nil {
t.Fatalf("unexpected error: %v", err)
}
assertMatch(t, m, []byte("a03C"))
})
t.Run("Read throws EOFs after match", func(t *testing.T) {
pattern := "ABCDE"
re := regexp.MustCompile(pattern)
var buf bytes.Buffer
m := NewPatternMatchingReader(&buf, re, len(pattern))
assertMatch(t, m, nil)
buf.Write([]byte("ABCDE"))
p := make([]byte, 1024)
if _, err := m.Read(p); err != nil {
t.Fatalf("unexpected error: %v", err)
}
assertMatch(t, m, []byte(pattern))
buf.Write([]byte("FGHIJK"))
p = make([]byte, 1024)
if _, err := m.Read(p); err != io.EOF {
t.Fatalf("unexpected error: expected EOF; got %v", err)
}
assertMatch(t, m, []byte(pattern))
})
}
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 := NewPatternMatchingReader(r, regexp.MustCompile("ABCD(E|F)"), 5)
go func() {
w.Write([]byte("ABC"))
w.Write([]byte("D"))
w.Write([]byte("EFGH"))
}()
match, err := ReadUntilMatch(context.Background(), m, nil)
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 := NewSequenceMatchingReader(r, "foo")
go func() {
w.Write([]byte("bar"))
w.Close()
}()
_, err := ReadUntilMatch(context.Background(), m, nil)
if !errors.Is(err, io.EOF) {
t.Errorf("ReadUntilMatch() returned %v, expected io.EOF", err)
}
})
}