blob: d23cb4d2975ceea71e80004a03c972d991c756fa [file] [log] [blame]
// Copyright 2015 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package renderer
import (
"bytes"
"io"
"testing"
"go.chromium.org/luci/logdog/api/logpb"
. "github.com/smartystreets/goconvey/convey"
)
// testSource implements the Source interface using stubbed data.
type testSource struct {
logs []*logpb.LogEntry
err error
}
func (ts *testSource) NextLogEntry() (le *logpb.LogEntry, err error) {
if ts.err != nil {
return nil, ts.err
}
if len(ts.logs) > 0 {
le, ts.logs = ts.logs[0], ts.logs[1:]
}
if len(ts.logs) == 0 {
err = io.EOF
}
return
}
func (ts *testSource) loadLogEntry(le *logpb.LogEntry) {
ts.logs = append(ts.logs, le)
}
func (ts *testSource) loadText(line, delim string) {
ts.loadLogEntry(&logpb.LogEntry{
Content: &logpb.LogEntry_Text{
Text: &logpb.Text{
Lines: []*logpb.Text_Line{
{
Value: []byte(line),
Delimiter: delim,
},
},
},
},
})
}
func (ts *testSource) loadBinary(data []byte) {
ts.loadLogEntry(&logpb.LogEntry{
Content: &logpb.LogEntry_Binary{
Binary: &logpb.Binary{
Data: data,
},
},
})
}
func (ts *testSource) loadDatagram(data []byte, term bool) {
ts.loadLogEntry(&logpb.LogEntry{
Content: &logpb.LogEntry_Datagram{
Datagram: &logpb.Datagram{
Partial: &logpb.Datagram_Partial{
Last: term,
},
Data: data,
},
},
})
}
func TestRenderer(t *testing.T) {
t.Parallel()
Convey(`A Renderer connected to a test Source`, t, func() {
ts := testSource{}
b := bytes.Buffer{}
r := &Renderer{
Source: &ts,
}
Convey(`With no log data, will render nothing and return.`, func() {
c, err := b.ReadFrom(r)
So(err, ShouldBeNil)
So(c, ShouldEqual, 0)
})
Convey(`With TEXT log entries ["1", "2"] using "DELIM" as the delimiter`, func() {
ts.loadText("1", "DELIM")
ts.loadText("2", "DELIM")
Convey(`When not configured to render raw, renders "1\n2\n".`, func() {
_, err := b.ReadFrom(r)
So(err, ShouldBeNil)
So(b.String(), ShouldEqual, "1\n2\n")
})
Convey(`When configured to render raw, renders "1DELIM2DELIM".`, func() {
r.Raw = true
_, err := b.ReadFrom(r)
So(err, ShouldBeNil)
So(b.String(), ShouldEqual, "1DELIM2DELIM")
})
})
Convey(`With BINARY log entries {{0x00}, {0x01, 0x02}, {}, {0x03}}`, func() {
ts.loadBinary([]byte{0x00})
ts.loadBinary([]byte{0x01, 0x02})
ts.loadBinary([]byte{})
ts.loadBinary([]byte{0x03})
Convey(`Renders {0x00, 0x01, 0x02, 0x03}.`, func() {
_, err := b.ReadFrom(r)
So(err, ShouldBeNil)
So(b.Bytes(), ShouldResemble, []byte("00010203"))
})
Convey(`Renders raw {0x00, 0x01, 0x02, 0x03}.`, func() {
r.Raw = true
_, err := b.ReadFrom(r)
So(err, ShouldBeNil)
So(b.Bytes(), ShouldResemble, []byte{0x00, 0x01, 0x02, 0x03})
})
Convey(`Can read the raw stream byte-by-byte.`, func() {
r.Raw = true
b := [1]byte{}
c, err := r.Read(b[:])
So(err, ShouldBeNil)
So(c, ShouldEqual, 1)
So(b[0], ShouldEqual, 0x00)
c, err = r.Read(b[:])
So(err, ShouldBeNil)
So(c, ShouldEqual, 1)
So(b[0], ShouldEqual, 0x01)
c, err = r.Read(b[:])
So(err, ShouldBeNil)
So(c, ShouldEqual, 1)
So(b[0], ShouldEqual, 0x02)
c, err = r.Read(b[:])
So(err, ShouldEqual, io.EOF)
So(c, ShouldEqual, 1)
So(b[0], ShouldEqual, 0x03)
})
})
Convey(`With partial DATAGRAM log entries {{0x00}, {0x01, 0x02}, {}, {0x03}}.`, func() {
ts.loadDatagram([]byte{0x00}, false)
ts.loadDatagram([]byte{0x01, 0x02}, false)
ts.loadDatagram([]byte{}, false)
ts.loadDatagram([]byte{0x03}, true)
hexDump := "Datagram #0 (4 bytes)\n" +
"00000000 00 01 02 03 |....|\n\n"
Convey(`Renders a full hex dump.`, func() {
_, err := b.ReadFrom(r)
So(err, ShouldBeNil)
So(b.String(), ShouldEqual, hexDump)
})
Convey(`When deferring to a datagram writer`, func() {
Convey(`Uses the writer instead of a hex dump.`, func() {
var bytes []byte
r.DatagramWriter = func(w io.Writer, dg []byte) bool {
bytes = make([]byte, len(dg))
copy(bytes, dg)
w.Write([]byte("rendered"))
return true
}
_, err := b.ReadFrom(r)
So(err, ShouldBeNil)
So(b.String(), ShouldEqual, "Datagram #0 (4 bytes)\nrendered\n")
So(bytes, ShouldResemble, []byte{0x00, 0x01, 0x02, 0x03})
})
Convey(`Renders a full hex dump when the writer returns false.`, func() {
r.DatagramWriter = func(w io.Writer, dg []byte) bool { return false }
_, err := b.ReadFrom(r)
So(err, ShouldBeNil)
So(b.String(), ShouldEqual, hexDump)
})
})
})
Convey(`With empty log entries, renders nothing.`, func() {
ts.loadLogEntry(&logpb.LogEntry{})
c, err := b.ReadFrom(r)
So(err, ShouldBeNil)
So(c, ShouldEqual, 0)
})
})
}