| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package streamserver |
| 6 |
| 7 import ( |
| 8 "bytes" |
| 9 "encoding/binary" |
| 10 "fmt" |
| 11 "io" |
| 12 "testing" |
| 13 "time" |
| 14 |
| 15 "github.com/luci/luci-go/client/logdog/butlerlib/streamclient" |
| 16 "github.com/luci/luci-go/client/logdog/butlerlib/streamproto" |
| 17 "github.com/luci/luci-go/common/clock/testclock" |
| 18 "github.com/luci/luci-go/common/logdog/protocol" |
| 19 "github.com/luci/luci-go/common/logdog/types" |
| 20 "github.com/luci/luci-go/common/proto/google" |
| 21 ta "github.com/luci/luci-go/common/testing/assertions" |
| 22 . "github.com/smartystreets/goconvey/convey" |
| 23 "golang.org/x/net/context" |
| 24 ) |
| 25 |
| 26 func writePanic(w io.Writer, d []byte) { |
| 27 amt, err := w.Write(d) |
| 28 if err != nil { |
| 29 panic(err) |
| 30 } |
| 31 if amt != len(d) { |
| 32 panic("failed to write full buffer") |
| 33 } |
| 34 } |
| 35 |
| 36 type handshakeBuilder struct { |
| 37 magic []byte // The frame header. If empty, don't include a frame header
. |
| 38 size uint64 // The size. If zero, calculate the size. |
| 39 } |
| 40 |
| 41 func (b *handshakeBuilder) writeTo(w io.Writer, handshake string, data []byte) { |
| 42 // Frame header |
| 43 if len(b.magic) > 0 { |
| 44 writePanic(w, b.magic) |
| 45 } |
| 46 |
| 47 // Size |
| 48 size := b.size |
| 49 if size == 0 { |
| 50 size = uint64(len(handshake)) |
| 51 } |
| 52 sizeBuf := make([]byte, binary.MaxVarintLen64) |
| 53 count := binary.PutUvarint(sizeBuf, uint64(size)) |
| 54 writePanic(w, sizeBuf[:count]) |
| 55 |
| 56 if handshake != "" { |
| 57 writePanic(w, []byte(handshake)) |
| 58 } |
| 59 writePanic(w, data) |
| 60 } |
| 61 |
| 62 // Generate a reader from the configuration. |
| 63 func (b *handshakeBuilder) reader(handshake string, data []byte) io.Reader { |
| 64 r := bytes.Buffer{} |
| 65 b.writeTo(&r, handshake, data) |
| 66 return &r |
| 67 } |
| 68 |
| 69 // Tests for getProtocolFromMagic |
| 70 func TestGetProtocolFromMagic(t *testing.T) { |
| 71 Convey(`Given a Reader that will emit the protocol header.`, t, func() { |
| 72 ctx := context.Background() |
| 73 hb := handshakeBuilder{ |
| 74 magic: streamclient.ProtocolFrameHeaderMagic, |
| 75 } |
| 76 dr := hb.reader("", nil) |
| 77 |
| 78 Convey(`Should return a protocol object.`, func() { |
| 79 protocol, err := getProtocolFromMagic(ctx, dr) |
| 80 So(err, ShouldBeNil) |
| 81 So(protocol, ShouldHaveSameTypeAs, &handshakeProtocolImp
l{}) |
| 82 }) |
| 83 }) |
| 84 |
| 85 Convey(`Given a Reader with not enough data.`, t, func() { |
| 86 ctx := context.Background() |
| 87 dr := bytes.NewReader([]byte{}) |
| 88 |
| 89 Convey(`Should return an error.`, func() { |
| 90 _, err := getProtocolFromMagic(ctx, dr) |
| 91 So(err, ShouldNotBeNil) |
| 92 }) |
| 93 }) |
| 94 |
| 95 Convey(`Given a Reader with invalid protocol data.`, t, func() { |
| 96 ctx := context.Background() |
| 97 dr := bytes.NewReader([]byte{0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA}
) |
| 98 |
| 99 Convey(`Should return an error.`, func() { |
| 100 _, err := getProtocolFromMagic(ctx, dr) |
| 101 So(err, ShouldNotBeNil) |
| 102 }) |
| 103 }) |
| 104 } |
| 105 |
| 106 // Tests for handshakeProtocol |
| 107 func testHandshakeProtocol(t *testing.T, verbose bool) { |
| 108 Convey(fmt.Sprintf(`A handshakeProtocol instance (verbose=%v)`, verbose)
, t, func() { |
| 109 var hb handshakeBuilder |
| 110 |
| 111 ctx, tc := testclock.UseTime(context.Background(), testclock.Tes
tTimeUTC) |
| 112 p := &handshakeProtocolImpl{} |
| 113 p.forceVerbose = verbose |
| 114 |
| 115 Convey(`Loading a handshake frame starting with an invalid size
varint value must fail.`, func() { |
| 116 // This exceeds the maximum 64-bit varint size (10 bytes
) and never |
| 117 // terminates (no MSB). |
| 118 _, err := p.Handshake(ctx, bytes.NewReader([]byte{ |
| 119 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, |
| 120 })) |
| 121 So(err, ShouldNotBeNil) |
| 122 }) |
| 123 |
| 124 Convey(`Loading a handshake frame larger than the maximum header
size must fail.`, func() { |
| 125 hb.size = maxHeaderSize + 1 |
| 126 _, err := p.Handshake(ctx, hb.reader("", nil)) |
| 127 So(err, ShouldNotBeNil) |
| 128 }) |
| 129 |
| 130 Convey(`Loading an JSON object with just a name`, func() { |
| 131 props, err := p.Handshake(ctx, hb.reader(`{"name": "test
"}`, nil)) |
| 132 So(err, ShouldBeNil) |
| 133 |
| 134 Convey(`Should produce a valid stream configuration.`, f
unc() { |
| 135 So(props, ta.ShouldResembleV, &streamproto.Prope
rties{ |
| 136 LogStreamDescriptor: protocol.LogStreamD
escriptor{ |
| 137 Name: "test", |
| 138 Timestamp: google.NewTimestamp
(tc.Now()), |
| 139 StreamType: protocol.LogStreamD
escriptor_TEXT, |
| 140 ContentType: string(types.Conten
tTypeText), |
| 141 }, |
| 142 }) |
| 143 }) |
| 144 }) |
| 145 |
| 146 Convey(`Loading a fully-specified configuration`, func() { |
| 147 data := `{ |
| 148 "name": "test", "tee": "stdout", "timestamp": "2
015-05-07T01:29:51+00:00", |
| 149 "contentType": "text/plain", |
| 150 "tags": {"foo": "bar", "baz": "qux"} |
| 151 }` |
| 152 props, err := p.Handshake(ctx, hb.reader(data, nil)) |
| 153 So(err, ShouldBeNil) |
| 154 |
| 155 Convey(`Should produce a specific configuration.`, func(
) { |
| 156 So(props, ta.ShouldResembleV, &streamproto.Prope
rties{ |
| 157 LogStreamDescriptor: protocol.LogStreamD
escriptor{ |
| 158 Name: "test", |
| 159 ContentType: "text/plain", |
| 160 Timestamp: google.NewTimestamp
(time.Date(2015, 05, 07, 1, 29, 51, 0, time.UTC)), |
| 161 Tags: []*protocol.LogStreamDescr
iptor_Tag{ |
| 162 {Key: "baz", Value: "qux
"}, |
| 163 {Key: "foo", Value: "bar
"}, |
| 164 }, |
| 165 }, |
| 166 Tee: streamproto.TeeStdout, |
| 167 }) |
| 168 }) |
| 169 }) |
| 170 |
| 171 Convey(`Loading a (valid) JSON array should fail to load.`, func
() { |
| 172 data := `["This is an array!"]` |
| 173 _, err := p.Handshake(ctx, hb.reader(data, nil)) |
| 174 So(err, ShouldNotBeNil) |
| 175 }) |
| 176 |
| 177 Convey(`Loading a JSON descriptor with just a name should succee
d.`, func() { |
| 178 data := `{"name": "test"}` |
| 179 props, err := p.Handshake(ctx, hb.reader(data, nil)) |
| 180 So(err, ShouldBeNil) |
| 181 So(props, ShouldNotBeNil) |
| 182 }) |
| 183 |
| 184 Convey(`Loading an empty JSON object with a larger-than-necessar
y header size should fail.`, func() { |
| 185 data := `{}` |
| 186 hb.size = uint64(len(data) + 10) |
| 187 _, err := p.Handshake(ctx, hb.reader(data, nil)) |
| 188 So(err, ShouldNotBeNil) |
| 189 }) |
| 190 |
| 191 Convey(`Loading an JSON with an erroneous config should fail.`,
func() { |
| 192 data := `{"timestamp": "text-for-some-reason"}` |
| 193 _, err := p.Handshake(ctx, hb.reader(data, nil)) |
| 194 So(err, ShouldNotBeNil) |
| 195 }) |
| 196 |
| 197 Convey(`Loading an invalid JSON descriptor should fail.`, func()
{ |
| 198 data := `invalid` |
| 199 _, err := p.Handshake(ctx, hb.reader(data, nil)) |
| 200 So(err, ShouldNotBeNil) |
| 201 }) |
| 202 }) |
| 203 |
| 204 for idx, v := range []string{"none", "stdout", "stderr", "clearly invali
d"} { |
| 205 Convey(fmt.Sprintf(`A protocol with a tee type of: %s`, v), t, f
unc() { |
| 206 ctx := context.Background() |
| 207 var hb handshakeBuilder |
| 208 data := fmt.Sprintf(`{ |
| 209 "name": "test", |
| 210 "tee": "%s" |
| 211 }`, v) |
| 212 p := &handshakeProtocolImpl{} |
| 213 _, err := p.Handshake(ctx, hb.reader(data, nil)) |
| 214 if idx <= 2 { |
| 215 Convey(`Should successfully parse.`, func() { |
| 216 So(err, ShouldBeNil) |
| 217 }) |
| 218 } else { |
| 219 Convey(`Should fail to parse.`, func() { |
| 220 So(err, ShouldNotBeNil) |
| 221 }) |
| 222 } |
| 223 }) |
| 224 } |
| 225 } |
| 226 |
| 227 func TestHandshakeProtocol(t *testing.T) { |
| 228 testHandshakeProtocol(t, false) |
| 229 } |
| 230 |
| 231 // As an optimization, we buffer data differently for verbose output. This |
| 232 // creates a separate code path that we have to take if logging verbose function |
| 233 // is set. |
| 234 func TestHandshakeProtocolVerbose(t *testing.T) { |
| 235 testHandshakeProtocol(t, true) |
| 236 } |
| OLD | NEW |