package textedit_test import ( "bytes" "code.squareroundforest.org/arpio/textedit" "errors" "io" "testing" ) var errTest = errors.New("test") type failingWriter struct { out io.Writer fail bool } type flusherWriter struct { out io.Writer fail bool flushed bool } type flusherWriterNoErr struct { out io.Writer flushed bool } func (w *failingWriter) Write(p []byte) (int, error) { if w.fail { return 0, errTest } return w.out.Write(p) } func (w *flusherWriter) Write(p []byte) (int, error) { return w.out.Write(p) } func (w *flusherWriter) Flush() error { w.flushed = true if w.fail { return errTest } return nil } func (w *flusherWriterNoErr) Write(p []byte) (int, error) { return w.out.Write(p) } func (w *flusherWriterNoErr) Flush() { w.flushed = true } func TestNoop(t *testing.T) { t.Run("editor", func(t *testing.T) { var b bytes.Buffer w := textedit.New(&b, textedit.Func(nil, func(int) []rune { return nil })) w.Write([]byte("foo bar baz")) w.Flush() if b.String() != "foo bar baz" { t.Fatal(b.String()) } }) t.Run("release", func(t *testing.T) { var b bytes.Buffer w := textedit.New(&b, textedit.Func(func(r rune, s int) ([]rune, int) { return []rune{r}, 9 }, nil)) w.Write([]byte("foo bar baz")) w.Flush() if b.String() != "foo bar baz" { t.Fatal(b.String()) } }) } func TestWriteRune(t *testing.T) { var b bytes.Buffer w := textedit.New(&b, textedit.Replace("o", "e")) for _, r := range []rune("foo bar baz") { w.WriteRune(r) } w.Flush() if b.String() != "fee bar baz" { t.Fatal(b.String()) } } func TestBrokenUnicode(t *testing.T) { var b bytes.Buffer w := textedit.New(&b) w.Write([]byte("foo \xc2bar baz")) w.Flush() if b.String() != "foo bar baz" { t.Fatal(b.String()) } } func TestFailingWriter(t *testing.T) { t.Run("after write", func(t *testing.T) { var b bytes.Buffer fw := failingWriter{out: &b} w := textedit.New(&fw, textedit.Replace("o", "e")) if _, err := w.Write([]byte("foo ")); err != nil { t.Fatal(err) } fw.fail = true if _, err := w.Write([]byte("bar")); !errors.Is(err, errTest) { t.Fatal("failed to fail with the right error", err) } if _, err := w.Write([]byte("bar")); !errors.Is(err, errTest) { t.Fatal("failed to fail with the right error", err) } if err := w.Flush(); !errors.Is(err, errTest) { t.Fatal("failed to fail with the right error", err) } }) t.Run("after flush", func(t *testing.T) { var b bytes.Buffer fw := failingWriter{out: &b} w := textedit.New(&fw, textedit.Replace("o", "e")) if _, err := w.Write([]byte("foo bar")); err != nil { t.Fatal(err) } fw.fail = true if err := w.Flush(); !errors.Is(err, errTest) { t.Fatal("failed to fail with the right error", err) } }) } func TestFlush(t *testing.T) { t.Run("with err", func(t *testing.T) { var b bytes.Buffer fw := flusherWriter{out: &b} w := textedit.New(&fw, textedit.Replace("o", "e")) if _, err := w.Write([]byte("foo bar")); err != nil { t.Fatal(err) } if err := w.Flush(); err != nil { t.Fatal(err) } if b.String() != "fee bar" { t.Fatal(b.String()) } if fw.flushed { t.Fatal("should not flush") } }) t.Run("without err", func(t *testing.T) { var b bytes.Buffer fw := flusherWriterNoErr{out: &b} w := textedit.New(&fw, textedit.Replace("o", "e")) if _, err := w.Write([]byte("foo bar")); err != nil { t.Fatal(err) } if err := w.Flush(); err != nil { t.Fatal(err) } if b.String() != "fee bar" { t.Fatal(b.String()) } if fw.flushed { t.Fatal("should not flush") } }) } func TestInit(t *testing.T) { edit := func(r rune, lineStart bool) ([]rune, bool) { if r == '\n' { return []rune{'\n'}, true } if lineStart { return append([]rune("> "), r), false } return []rune{r}, false } var b bytes.Buffer e := textedit.FuncInit(edit, func(bool) []rune { return nil }, true) w := textedit.New(&b, e) if _, err := w.Write([]byte("quoted\nemail")); err != nil { t.Fatal(err) } if b.String() != "> quoted\n> email" { t.Fatal(b.String()) } }