1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package sdjournal
17
18 import (
19 "bytes"
20 "errors"
21 "fmt"
22 "io"
23 "io/ioutil"
24 "math/rand"
25 "os"
26 "strings"
27 "testing"
28 "time"
29
30 "github.com/coreos/go-systemd/v22/journal"
31 )
32
33 func TestJournalFollow(t *testing.T) {
34 r, err := NewJournalReader(JournalReaderConfig{
35 Since: time.Duration(-15) * time.Second,
36 Matches: []Match{
37 {
38 Field: SD_JOURNAL_FIELD_SYSTEMD_UNIT,
39 Value: "NetworkManager.service",
40 },
41 },
42 })
43
44 if err != nil {
45 t.Fatalf("Error opening journal: %s", err)
46 }
47
48 if r == nil {
49 t.Fatal("Got a nil reader")
50 }
51
52 defer r.Close()
53
54
55 done := make(chan struct{}, 1)
56 errCh := make(chan error, 1)
57 defer close(done)
58 go func() {
59 for {
60 select {
61 case <-done:
62 return
63 default:
64 if perr := journal.Print(journal.PriInfo, "test message %s", time.Now()); err != nil {
65 errCh <- perr
66 return
67 }
68
69 time.Sleep(time.Second)
70 }
71 }
72 }()
73
74
75 timeout := time.Duration(5) * time.Second
76 if err = r.Follow(time.After(timeout), os.Stdout); err != ErrExpired {
77 t.Fatalf("Error during follow: %s", err)
78 }
79
80 select {
81 case err := <-errCh:
82 t.Fatalf("Error writing to journal: %s", err)
83 default:
84 }
85 }
86
87 func TestJournalWait(t *testing.T) {
88 id := time.Now().String()
89 j, err := NewJournal()
90 if err != nil {
91 t.Fatalf("Error opening journal: %s", err)
92 }
93 if err := j.AddMatch("TEST=TestJournalWait " + id); err != nil {
94 t.Fatalf("Error adding match: %s", err)
95 }
96 if err := j.SeekTail(); err != nil {
97 t.Fatalf("Error seeking to tail: %s", err)
98 }
99 if _, err := j.Previous(); err != nil {
100 t.Fatalf("Error retrieving previous entry: %s", err)
101 }
102
103 var t1, t2 time.Time
104 for ret := -1; ret != SD_JOURNAL_NOP; {
105
106
107 t1 = time.Now()
108 ret = j.Wait(time.Millisecond * 300)
109 t2 = time.Now()
110 }
111 duration := t2.Sub(t1)
112
113 if duration > time.Millisecond*325 || duration < time.Millisecond*300 {
114 t.Errorf("Wait did not wait 300ms. Actually waited %s", duration.String())
115 }
116
117 journal.Send("test message", journal.PriInfo, map[string]string{"TEST": "TestJournalWait " + id})
118 for ret := -1; ret != SD_JOURNAL_APPEND; {
119 t1 = time.Now()
120 ret = j.Wait(time.Millisecond * 300)
121 t2 = time.Now()
122 }
123 duration = t2.Sub(t1)
124
125 if duration >= time.Millisecond*300 {
126 t.Errorf("Wait took longer than 300ms. Actual duration %s", duration.String())
127 }
128 }
129
130 func TestJournalGetUsage(t *testing.T) {
131 j, err := NewJournal()
132
133 if err != nil {
134 t.Fatalf("Error opening journal: %s", err)
135 }
136
137 if j == nil {
138 t.Fatal("Got a nil journal")
139 }
140
141 defer j.Close()
142
143 _, err = j.GetUsage()
144
145 if err != nil {
146 t.Fatalf("Error getting journal size: %s", err)
147 }
148 }
149
150 func TestJournalCursorGetSeekAndTest(t *testing.T) {
151 j, err := NewJournal()
152 if err != nil {
153 t.Fatalf("Error opening journal: %s", err)
154 }
155
156 if j == nil {
157 t.Fatal("Got a nil journal")
158 }
159
160 defer j.Close()
161
162 err = journal.Print(journal.PriInfo, "test message for cursor %s", time.Now())
163 if err != nil {
164 t.Fatalf("Error writing to journal: %s", err)
165 }
166
167 if err = waitAndNext(j); err != nil {
168 t.Fatalf(err.Error())
169 }
170
171 c, err := j.GetCursor()
172 if err != nil {
173 t.Fatalf("Error getting cursor from journal: %s", err)
174 }
175
176 err = j.SeekCursor(c)
177 if err != nil {
178 t.Fatalf("Error seeking cursor to journal: %s", err)
179 }
180
181 if err = waitAndNext(j); err != nil {
182 t.Fatalf(err.Error())
183 }
184
185 err = j.TestCursor(c)
186 if err != nil {
187 t.Fatalf("Error testing cursor to journal: %s", err)
188 }
189
190 err = journal.Print(journal.PriInfo, "second message %s", time.Now())
191 if err != nil {
192 t.Fatalf("Error writing to journal: %s", err)
193 }
194
195 if err = waitAndNext(j); err != nil {
196 t.Fatalf(err.Error())
197 }
198
199 err = j.TestCursor(c)
200 if err != ErrNoTestCursor {
201 t.Fatalf("Error, TestCursor should fail because current cursor has moved from the previous obtained cursor")
202 }
203 }
204
205 func TestNewJournalFromDir(t *testing.T) {
206
207 dir := "/ClearlyNonExistingPath/"
208 j, err := NewJournalFromDir(dir)
209 if err == nil {
210 defer j.Close()
211 t.Fatalf("Error expected when opening dummy path (%s)", dir)
212 }
213
214 dir, err = ioutil.TempDir("", "go-systemd-test")
215 if err != nil {
216 t.Fatalf("Error creating tempdir: %s", err)
217 }
218 defer os.RemoveAll(dir)
219 j, err = NewJournalFromDir(dir)
220 if err != nil {
221 t.Fatalf("Error opening journal: %s", err)
222 }
223 if j == nil {
224 t.Fatal("Got a nil journal")
225 }
226 j.Close()
227 }
228
229 func setupJournalRoundtrip() (*Journal, map[string]string, error) {
230 j, err := NewJournal()
231 if err != nil {
232 return nil, nil, fmt.Errorf("Error opening journal: %s", err)
233 }
234
235 if j == nil {
236 return nil, nil, fmt.Errorf("Got a nil journal")
237 }
238
239 j.FlushMatches()
240
241 matchField := "TESTJOURNALENTRY"
242 matchValue := fmt.Sprintf("%d", time.Now().UnixNano())
243 m := Match{Field: matchField, Value: matchValue}
244 err = j.AddMatch(m.String())
245 if err != nil {
246 return nil, nil, fmt.Errorf("Error adding matches to journal: %s", err)
247 }
248
249 msg := fmt.Sprintf("test journal get entry message %s", time.Now())
250 data := map[string]string{matchField: matchValue}
251 err = journal.Send(msg, journal.PriInfo, data)
252 if err != nil {
253 return nil, nil, fmt.Errorf("Error writing to journal: %s", err)
254 }
255
256 time.Sleep(time.Duration(1) * time.Second)
257
258 n, err := j.Next()
259 if err != nil {
260 return nil, nil, fmt.Errorf("Error reading from journal: %s", err)
261 }
262
263 if n == 0 {
264 return nil, nil, fmt.Errorf("Error reading from journal: %s", io.EOF)
265 }
266
267 data["MESSAGE"] = msg
268
269 return j, data, nil
270 }
271
272 func TestJournalGetData(t *testing.T) {
273 j, wantEntry, err := setupJournalRoundtrip()
274 if err != nil {
275 t.Fatal(err.Error())
276 }
277
278 defer j.Close()
279
280 for k, v := range wantEntry {
281 data := fmt.Sprintf("%s=%s", k, v)
282
283 dataStr, err := j.GetData(k)
284 if err != nil {
285 t.Fatalf("GetData() error: %v", err)
286 }
287
288 if dataStr != data {
289 t.Fatalf("Invalid data for \"%s\": got %s, want %s", k, dataStr, data)
290 }
291
292 dataBytes, err := j.GetDataBytes(k)
293 if err != nil {
294 t.Fatalf("GetDataBytes() error: %v", err)
295 }
296
297 if string(dataBytes) != data {
298 t.Fatalf("Invalid data bytes for \"%s\": got %s, want %s", k, string(dataBytes), data)
299 }
300
301 valStr, err := j.GetDataValue(k)
302 if err != nil {
303 t.Fatalf("GetDataValue() error: %v", err)
304 }
305
306 if valStr != v {
307 t.Fatalf("Invalid data value for \"%s\": got %s, want %s", k, valStr, v)
308 }
309
310 valBytes, err := j.GetDataValueBytes(k)
311 if err != nil {
312 t.Fatalf("GetDataValueBytes() error: %v", err)
313 }
314
315 if string(valBytes) != v {
316 t.Fatalf("Invalid data value bytes for \"%s\": got %s, want %s", k, string(valBytes), v)
317 }
318 }
319 }
320
321 func TestJournalGetEntry(t *testing.T) {
322 j, wantEntry, err := setupJournalRoundtrip()
323 if err != nil {
324 t.Fatal(err.Error())
325 }
326
327 defer j.Close()
328
329 entry, err := j.GetEntry()
330 if err != nil {
331 t.Fatalf("Error getting the entry to journal: %s", err)
332 }
333
334 for k, wantV := range wantEntry {
335 gotV := entry.Fields[k]
336 if gotV != wantV {
337 t.Fatalf("Bad result for entry.Fields[\"%s\"]: got %s, want %s", k, gotV, wantV)
338 }
339 }
340 }
341
342
343
344 func TestJournalReaderSmallReadBuffer(t *testing.T) {
345
346 delim := "%%%%%%"
347 longEntry := strings.Repeat("a", 256)
348 matchField := "TESTJOURNALREADERSMALLBUF"
349 matchValue := fmt.Sprintf("%d", time.Now().UnixNano())
350 r, err := NewJournalReader(JournalReaderConfig{
351 Since: time.Duration(-15) * time.Second,
352 Matches: []Match{
353 {
354 Field: matchField,
355 Value: matchValue,
356 },
357 },
358 })
359 if err != nil {
360 t.Fatalf("Error opening journal: %s", err)
361 }
362 if r == nil {
363 t.Fatal("Got a nil reader")
364 }
365 defer r.Close()
366
367 want := fmt.Sprintf("%slongentry %s%s", delim, longEntry, delim)
368 err = journal.Send(want, journal.PriInfo, map[string]string{matchField: matchValue})
369 if err != nil {
370 t.Fatal("Error writing to journal", err)
371 }
372 time.Sleep(time.Second)
373
374
375 finalBuff := new(bytes.Buffer)
376 var e error
377 for c := -1; c != 0 && e == nil; {
378 smallBuf := make([]byte, 5)
379 c, e = r.Read(smallBuf)
380 if c > len(smallBuf) {
381 t.Fatalf("Got unexpected read length: %d vs %d", c, len(smallBuf))
382 }
383 _, _ = finalBuff.Write(smallBuf)
384 }
385 b := finalBuff.String()
386 got := strings.Split(b, delim)
387 if len(got) != 3 {
388 t.Fatalf("Got unexpected entry %s", b)
389 }
390 if got[1] != strings.Trim(want, delim) {
391 t.Fatalf("Got unexpected message %s", got[1])
392 }
393 }
394
395 func TestJournalGetUniqueValues(t *testing.T) {
396 j, err := NewJournal()
397 if err != nil {
398 t.Fatal(err)
399 }
400
401 defer j.Close()
402
403 uniqueString := generateRandomField(20)
404 testEntries := []string{"A", "B", "C", "D"}
405 for _, v := range testEntries {
406 err = journal.Send("TEST: "+uniqueString, journal.PriInfo, map[string]string{uniqueString: v})
407 if err != nil {
408 t.Fatal(err)
409 }
410 }
411
412
413 time.Sleep(time.Millisecond * 500)
414
415 values, err := j.GetUniqueValues(uniqueString)
416 if err != nil {
417 t.Fatal(err)
418 }
419
420 if len(values) != len(testEntries) {
421 t.Fatalf("Expect %d entries. Got %d", len(testEntries), len(values))
422 }
423
424 if !contains(values, "A") || !contains(values, "B") || !contains(values, "C") || !contains(values, "D") {
425 t.Fatalf("Expect 4 values for %s field: A,B,C,D. Got %s", uniqueString, values)
426 }
427 }
428
429 func TestJournalGetCatalog(t *testing.T) {
430 want := []string{
431 "Subject: ",
432 "Defined-By: systemd",
433 "Support: ",
434 }
435 j, err := NewJournal()
436 if err != nil {
437 t.Fatalf("Error opening journal: %s", err)
438 }
439
440 if j == nil {
441 t.Fatal("Got a nil journal")
442 }
443
444 defer j.Close()
445
446 if err = j.SeekHead(); err != nil {
447 t.Fatalf("Seek to head failed: %s", err)
448 }
449
450 matchField := SD_JOURNAL_FIELD_SYSTEMD_UNIT
451 m := Match{Field: matchField, Value: "systemd-journald.service"}
452 if err = j.AddMatch(m.String()); err != nil {
453 t.Fatalf("Error adding matches to journal: %s", err)
454 }
455
456 if err = waitAndNext(j); err != nil {
457 t.Fatalf(err.Error())
458 }
459
460 catalog, err := j.GetCatalog()
461
462 if err != nil {
463 t.Fatalf("Failed to retrieve catalog entry: %s", err)
464 }
465
466 for _, w := range want {
467 if !strings.Contains(catalog, w) {
468 t.Fatalf("Failed to find \"%s\" in \n%s", w, catalog)
469 }
470 }
471 }
472
473 func TestJournalGetBootID(t *testing.T) {
474 j, err := NewJournal()
475 if err != nil {
476 t.Fatal(err)
477 }
478
479 defer j.Close()
480
481 bootID, err := j.GetBootID()
482
483 if err != nil {
484 t.Fatalf("Failed to get bootID : %s", err)
485 }
486
487 if len(bootID) <= 0 {
488 t.Fatalf("Get bootID: %s is Null", bootID)
489 }
490
491 fmt.Printf("Test GetBootID: %s", bootID)
492 }
493
494 func contains(s []string, v string) bool {
495 for _, entry := range s {
496 if entry == v {
497 return true
498 }
499 }
500 return false
501 }
502
503 func generateRandomField(n int) string {
504 letters := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
505 s := make([]rune, n)
506 rand.Seed(time.Now().UnixNano())
507 for i := range s {
508 s[i] = letters[rand.Intn(len(letters))]
509 }
510 return string(s)
511 }
512
513 func waitAndNext(j *Journal) error {
514 r := j.Wait(time.Duration(1) * time.Second)
515 if r < 0 {
516 return errors.New("Error waiting to journal")
517 }
518
519 n, err := j.Next()
520 if err != nil {
521 return fmt.Errorf("Error reading to journal: %s", err)
522 }
523
524 if n == 0 {
525 return fmt.Errorf("Error reading to journal: %s", io.EOF)
526 }
527
528 return nil
529 }
530
View as plain text