package reader_test import ( "bufio" "fmt" "io" "strings" "testing" "git.makaay.nl/mauricem/go-parsekit/reader" "github.com/stretchr/testify/assert" ) func ExampleNew() { r := reader.New(strings.NewReader("Hello, world!")) at := func(i int) rune { r, _ := r.RuneAt(i); return r } fmt.Printf("%c", at(0)) fmt.Printf("%c", at(12)) // Output: // H! } func TestNew_VariousInputTypesCanBeUsed(t *testing.T) { for _, test := range []struct { name string input interface{} }{ {"string", "Hello, world!"}, {"io.Reader", strings.NewReader("Hello, world!")}, {"*bufio.Reader", bufio.NewReader(strings.NewReader("Hello, world!"))}, {"bufio.Reader", *(bufio.NewReader(strings.NewReader("Hello, world!")))}, } { r := reader.New(test.input) firstRune, _ := r.RuneAt(0) if firstRune != 'H' { t.Errorf("[%s] first rune not 'H'", test.name) } lastRune, _ := r.RuneAt(12) if lastRune != '!' { t.Errorf("[%s] last rune not '!'", test.name) } } } func TestNew_UnhandledInputType_Panics(t *testing.T) { assert.PanicsWithValue(t, "parsekit.reader.New(): no support for input of type int", func() { reader.New(12345) }) } func TestReader_RuneAt(t *testing.T) { r := reader.New(strings.NewReader("Hello, world!")) at := func(i int) rune { r, _ := r.RuneAt(i); return r } // It is possible to go back and forth while reading the input. result := fmt.Sprintf("%c%c%c%c", at(0), at(12), at(7), at(0)) assert.Equal(t, "H!wH", result) } func TestReader_RuneAt_endOfFile(t *testing.T) { r := reader.New(strings.NewReader("Hello, world!")) rn, err := r.RuneAt(13) result := fmt.Sprintf("%q %s %t", rn, err, err == io.EOF) assert.Equal(t, "'�' EOF true", result) rn, err = r.RuneAt(20) result = fmt.Sprintf("%q %s %t", rn, err, err == io.EOF) assert.Equal(t, "'�' EOF true", result) } func TestReader_RuneAt_invalidRune(t *testing.T) { r := reader.New(strings.NewReader("Hello, \xcdworld!")) at := func(i int) rune { r, _ := r.RuneAt(i); return r } result := fmt.Sprintf("%c%c%c%c", at(6), at(7), at(8), at(9)) assert.Equal(t, " �wo", result, "result") } func ExampleReader_RuneAt() { reader := reader.New(strings.NewReader("Hello, world!")) fmt.Printf("Runes: ") for i := 0; ; i++ { r, err := reader.RuneAt(i) if err != nil { fmt.Printf("\nErr: %s\n", err) break } fmt.Printf("%c", r) } // Output: // Runes: Hello, world! // Err: EOF } func TestRuneAt_SkipsBOMAtStartOfFile(t *testing.T) { r := reader.New(strings.NewReader("\uFEFFBommetje!")) b, _ := r.RuneAt(0) o, _ := r.RuneAt(1) m, _ := r.RuneAt(2) bom := fmt.Sprintf("%c%c%c", b, o, m) assert.Equal(t, "Bom", bom, "first three runes") } func TestReader_Flush(t *testing.T) { r := reader.New(strings.NewReader("Hello, world!")) at := func(i int) rune { r, _ := r.RuneAt(i); return r } // Fills the buffer with the first 8 runes on the input: "Hello, w" result := fmt.Sprintf("%c", at(7)) assert.Equal(t, "w", result, "first read") // Now flush the first 4 runes from the buffer (dropping "Hell" from it) r.Flush(4) // Rune 0 is now pointing at what originally was rune offset 4. // We can continue reading from there. result = fmt.Sprintf("%c%c%c%c%c%c", at(0), at(1), at(2), at(3), at(4), at(5)) assert.Equal(t, "o, wor", result) } func ExampleReader_Flush() { r := reader.New(strings.NewReader("dog eat dog!")) at := func(offset int) rune { c, _ := r.RuneAt(offset); return c } // Read from the first 4 runes of the input. fmt.Printf("%c%c%c%c", at(0), at(1), at(2), at(3)) // Flush those 4 runes, bringing offset 0 to the start of "eat dog". r.Flush(4) // Read another 4 runes, because of the flushing, we start at offset 0. fmt.Printf("%c%c%c%c", at(1), at(2), at(0), at(3)) // Again, flush 4 runes, bringing offset 0 to the start of "dog!". r.Flush(4) // Read from the remainder runes. fmt.Printf("%c%c%c%c%c", at(2), at(1), at(1), at(0), at(3)) // Output: // dog ate good! } func TestGivenNumberOfRunesTooHigh_Flush_Panics(t *testing.T) { r := reader.New(strings.NewReader("Hello, world!")) // Fill buffer with "Hello, worl", the first 11 runes. r.RuneAt(10) // However, we flush 12 runes, which exceeds the buffer size. assert.PanicsWithValue(t, "parsekit.Input.Reader.Flush(): number of runes to flush "+ "(12) exceeds size of the buffer (11)", func() { r.Flush(12) }) }