Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(170)

Side by Side Diff: client/internal/logdog/butler/bundler/bundler_test.go

Issue 1276923003: logdog: Add bundler library. (Closed) Base URL: https://github.com/luci/luci-go@logdog-review-streamserver
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 bundler
6
7 import (
8 "crypto/md5"
9 "encoding/hex"
10 "fmt"
11 "strings"
12 "testing"
13 "time"
14
15 "github.com/golang/protobuf/proto"
16 "github.com/luci/luci-go/common/logdog/protocol"
17 "github.com/luci/luci-go/common/logdog/protocol/protoutil"
18 . "github.com/smartystreets/goconvey/convey"
19 )
20
21 var (
22 testNow = time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)
23 )
24
25 // fakeSizer is a Sizer implementation that counts (obviously incorrect) fixed
26 // sizes for each entry type.
27 type fakeSizer struct {
28 Bundle int
29 BundleEntry int
30 LogEntry int
31
32 size int
33 }
34
35 func (s *fakeSizer) Size() int {
36 return s.Bundle + s.size
37 }
38
39 func (s *fakeSizer) AppendBundleEntry(*protocol.ButlerLogBundle_Entry) {
40 s.size += s.BundleEntry
41 }
42
43 func (s *fakeSizer) AppendLogEntry(*protocol.ButlerLogBundle_Entry, *protocol.Lo gEntry) {
44 s.size += s.LogEntry
45 }
46
47 func hash(s, t string) []byte {
48 sum := md5.Sum([]byte(fmt.Sprintf("%s::%s", s, t)))
49 return sum[:]
50 }
51
52 func key(s, t string) string {
53 return hex.EncodeToString(hash(s, t))
54 }
55
56 // addEntry generates a ButlerLogBundle_Entry and appends it to our Bundler via
57 // one or more calls to Append.
58 //
59 // If "le" strings are supplied, those will create generated LogEntry for that
60 // ButlerLogBundle_Entry.
61 func gen(e string, t bool, le ...string) *protocol.ButlerLogBundle_Entry {
62 secret := hash(e, "secret")
63 name := key(e, "name")
64 contentType := "test/data"
65
66 be := &protocol.ButlerLogBundle_Entry{
67 Desc: &protocol.LogStreamDescriptor{
68 Prefix: &e,
69 Name: &name,
70 ContentType: &contentType,
71 Timestamp: protoutil.NewTimestamp(testNow),
72 },
73 Secret: secret,
74 Terminal: &t,
75 }
76
77 if len(le) > 0 {
78 be.Logs = make([]*protocol.LogEntry, len(le))
79 for i, l := range le {
80 be.Logs[i] = &protocol.LogEntry{
81 Lines: []string{
82 l,
83 },
84 Data: [][]byte{
85 hash(l, "data0"),
86 hash(l, "data1"),
87 hash(l, "data2"),
88 },
89 }
90 }
91 }
92 return be
93 }
94
95 func logEntryName(le *protocol.LogEntry) string {
96 if len(le.GetLines()) != 1 {
97 return ""
98 }
99 return le.GetLines()[0]
100 }
101
102 // "expected" is a notation to express a bundle entry and its keys:
103 // "a": a bundle entry keyed on "a".
104 // "+a": a terminal bundle entry keyed on "a".
105 // "a:1:2:3": a bundle entry keyed on "a" with three log entries, each keyed o n
106 // "1", "2", and "3" respectively.
107 func shouldHaveBundleEntries(actual interface{}, expected ...interface{}) string {
108 bundle := actual.(*protocol.ButlerLogBundle)
109
110 errors := []string{}
111 fail := func(f string, args ...interface{}) {
112 errors = append(errors, fmt.Sprintf(f, args...))
113 }
114
115 term := make(map[string]bool)
116 exp := make(map[string][]string)
117
118 // Parse expectation strings.
119 for _, e := range expected {
120 s := e.(string)
121 if len(s) == 0 {
122 continue
123 }
124
125 t := false
126 if s[0] == '+' {
127 t = true
128 s = s[1:]
129 }
130
131 parts := strings.Split(s, ":")
132 name := parts[0]
133 term[name] = t
134
135 if len(parts) > 1 {
136 exp[name] = append(exp[name], parts[1:]...)
137 }
138 }
139
140 entries := make(map[string]*protocol.ButlerLogBundle_Entry)
141 for _, be := range bundle.GetEntries() {
142 entries[be.GetDesc().GetPrefix()] = be
143 }
144 for name, t := range term {
145 be := entries[name]
146 if be == nil {
147 fail("No bundle entry for [%s]", name)
148 continue
149 }
150 delete(entries, name)
151
152 if t != be.GetTerminal() {
153 fail("Bundle entry [%s] doesn't match expected terminal state (exp: %v != act: %v)",
154 name, t, be.GetTerminal())
155 }
156
157 logs := exp[name]
158 for i, l := range logs {
159 if i > len(be.GetLogs()) {
160 fail("Bundle entry [%s] missing log: %s", name, l)
161 }
162 le := be.GetLogs()[i]
163
164 if logEntryName(le) != l {
165 fail("Bundle entry [%s] log %d doesn't match exp ected (exp: %s != act: %s)",
166 name, i, l, logEntryName(le))
167 }
168 }
169 for _, le := range be.GetLogs()[len(logs):] {
170 fail("Bundle entry [%s] has extra log entry: %s", name, logEntryName(le))
171 }
172 }
173 for k := range entries {
174 fail("Unexpected bundle entry present: [%s]", k)
175 }
176 return strings.Join(errors, "\n")
177 }
178
179 func TestBundler(t *testing.T) {
180 Convey(`An empty Bundler`, t, func() {
181 b := New(Config{}).(*bundlerImpl)
182
183 Convey(`Has a size of 0 and nil GetBundle() return value.`, func () {
184 So(b.Size(), ShouldEqual, 0)
185 So(b.Empty(), ShouldBeTrue)
186 So(b.GetBundles(0), ShouldBeNil)
187 })
188
189 Convey(`When adding an empty entry, still has size 0 and nil Get Bundle() return value.`, func() {
190 b.Append(gen("a", false))
191 So(b.Size(), ShouldEqual, 0)
192 So(b.Empty(), ShouldBeTrue)
193 So(b.GetBundles(0), ShouldBeNil)
194 })
195
196 Convey(`Bundles a terminal entry with no logs.`, func() {
197 b.Append(gen("a", true))
198
199 size, empty, bundles := b.Size(), b.Empty(), b.GetBundle s(0)
200 So(empty, ShouldBeFalse)
201
202 So(len(bundles), ShouldEqual, 1)
203 So(size, ShouldBeGreaterThanOrEqualTo, proto.Size(bundle s[0]))
204 So(bundles[0], shouldHaveBundleEntries, "+a")
205 })
206
207 Convey(`Bundles an entry with 3 logs.`, func() {
208 b.Append(gen("a", false, "1", "2"))
209 b.Append(gen("a", false, "3"))
210
211 size, empty, bundles := b.Size(), b.Empty(), b.GetBundle s(0)
212 So(empty, ShouldBeFalse)
213
214 So(len(bundles), ShouldEqual, 1)
215 So(size, ShouldBeGreaterThanOrEqualTo, proto.Size(bundle s[0]))
216 So(bundles[0], shouldHaveBundleEntries, "a:1:2:3")
217 })
218
219 Convey(`Bundles 2 entries with 2 logs each and one terminal entr y with no logs.`, func() {
220 b.Append(gen("a", false, "1", "2"))
221 b.Append(gen("b", false, "3", "4"))
222 b.Append(gen("c", true))
223 b.Append(gen("d", false))
224
225 size, empty, bundles := b.Size(), b.Empty(), b.GetBundle s(0)
226 So(empty, ShouldBeFalse)
227
228 So(len(bundles), ShouldEqual, 1)
229 So(size, ShouldBeGreaterThanOrEqualTo, proto.Size(bundle s[0]))
230 So(bundles[0], shouldHaveBundleEntries, "a:1:2", "b:3:4" , "+c")
231 })
232 })
233
234 Convey(`A Bundler with a fake Sizer`, t, func() {
235 b := New(Config{
236 NewSizer: func(*protocol.ButlerLogBundle) Sizer {
237 return &fakeSizer{
238 Bundle: 8,
239 BundleEntry: 2,
240 LogEntry: 5,
241 }
242 },
243 })
244 So(b.Size(), ShouldEqual, 8)
245
246 Convey(`Adding an entry with 5 log messages outputs two bundles. `, func() {
247 b.Append(gen("a", false, "1", "2", "3", "4", "5"))
248
249 bundles := b.GetBundles(20)
250 So(len(bundles), ShouldEqual, 3)
251 So(bundles[0], shouldHaveBundleEntries, "a:1:2")
252 So(bundles[1], shouldHaveBundleEntries, "a:3:4")
253 So(bundles[2], shouldHaveBundleEntries, "a:5")
254 })
255
256 Convey(`Adding two entries with 2 log messages each outputs firs t, then second.`, func() {
257 b.Append(gen("a", false, "1", "2"))
258 b.Append(gen("b", false, "3", "4"))
259
260 bundles := b.GetBundles(20)
261 So(len(bundles), ShouldEqual, 2)
262 So(bundles[0], shouldHaveBundleEntries, "a:1:2")
263 So(bundles[1], shouldHaveBundleEntries, "b:3:4")
264 })
265
266 Convey(`A non-terminal entry followed by a terminal version gets output as terminal.`, func() {
267 b.Append(gen("a", false, "1"))
268 b.Append(gen("a", true, "2"))
269
270 bundles := b.GetBundles(20)
271 So(len(bundles), ShouldEqual, 1)
272 So(bundles[0], shouldHaveBundleEntries, "+a:1:2")
273 })
274
275 Convey(`A terminal entry followed by a non-terminal version gets output as terminal.`, func() {
276 b.Append(gen("a", true, "1"))
277 b.Append(gen("a", false, "2"))
278
279 bundles := b.GetBundles(20)
280 So(len(bundles), ShouldEqual, 1)
281 So(bundles[0], shouldHaveBundleEntries, "+a:1:2")
282 })
283 })
284 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698