blob: 315dbc333c175104df44d4a60761a5b30b3e5498 [file] [log] [blame]
// Copyright 2020 The Monogon Project Authors.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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 logtree
import (
"fmt"
"strings"
"testing"
"time"
)
func expect(tree *LogTree, t *testing.T, dn DN, entries ...string) string {
t.Helper()
res, err := tree.Read(dn, WithChildren(), WithBacklog(BacklogAllAvailable))
if err != nil {
t.Fatalf("Read: %v", err)
}
if want, got := len(entries), len(res.Backlog); want != got {
t.Fatalf("wanted %v backlog entries, got %v", want, got)
}
got := make(map[string]bool)
for _, entry := range res.Backlog {
if entry.Leveled != nil {
got[entry.Leveled.MessagesJoined()] = true
}
if entry.Raw != nil {
got[entry.Raw.Data] = true
}
}
for _, entry := range entries {
if !got[entry] {
return fmt.Sprintf("missing entry %q", entry)
}
}
return ""
}
func TestMultiline(t *testing.T) {
tree := New()
// Two lines in a single message.
tree.MustLeveledFor("main").Info("foo\nbar")
// Two lines in a single message with a hanging newline that should get stripped.
tree.MustLeveledFor("main").Info("one\ntwo\n")
if res := expect(tree, t, "main", "foo\nbar", "one\ntwo"); res != "" {
t.Errorf("retrieval at main failed: %s", res)
}
}
func TestBacklog(t *testing.T) {
tree := New()
tree.MustLeveledFor("main").Info("hello, main!")
tree.MustLeveledFor("main.foo").Info("hello, main.foo!")
tree.MustLeveledFor("main.bar").Info("hello, main.bar!")
tree.MustLeveledFor("aux").Info("hello, aux!")
// No newline at the last entry - shouldn't get propagated to the backlog.
fmt.Fprintf(tree.MustRawFor("aux.process"), "processing foo\nprocessing bar\nbaz")
if res := expect(tree, t, "main", "hello, main!", "hello, main.foo!", "hello, main.bar!"); res != "" {
t.Errorf("retrieval at main failed: %s", res)
}
if res := expect(tree, t, "", "hello, main!", "hello, main.foo!", "hello, main.bar!", "hello, aux!", "processing foo", "processing bar"); res != "" {
t.Errorf("retrieval at root failed: %s", res)
}
if res := expect(tree, t, "aux", "hello, aux!", "processing foo", "processing bar"); res != "" {
t.Errorf("retrieval at aux failed: %s", res)
}
}
func TestStream(t *testing.T) {
tree := New()
tree.MustLeveledFor("main").Info("hello, backlog")
fmt.Fprintf(tree.MustRawFor("main.process"), "hello, raw backlog\n")
res, err := tree.Read("", WithBacklog(BacklogAllAvailable), WithChildren(), WithStream())
if err != nil {
t.Fatalf("Read: %v", err)
}
defer res.Close()
if want, got := 2, len(res.Backlog); want != got {
t.Errorf("wanted %d backlog item, got %d", want, got)
}
tree.MustLeveledFor("main").Info("hello, stream")
fmt.Fprintf(tree.MustRawFor("main.raw"), "hello, raw stream\n")
entries := make(map[string]bool)
timeout := time.After(time.Second * 1)
for {
done := false
select {
case <-timeout:
done = true
case p := <-res.Stream:
if p.Leveled != nil {
entries[p.Leveled.MessagesJoined()] = true
}
if p.Raw != nil {
entries[p.Raw.Data] = true
}
}
if done {
break
}
}
if entry := "hello, stream"; !entries[entry] {
t.Errorf("Missing entry %q", entry)
}
if entry := "hello, raw stream"; !entries[entry] {
t.Errorf("Missing entry %q", entry)
}
}
func TestVerbose(t *testing.T) {
tree := New()
tree.MustLeveledFor("main").V(10).Info("this shouldn't get logged")
reader, err := tree.Read("", WithBacklog(BacklogAllAvailable), WithChildren())
if err != nil {
t.Fatalf("Read: %v", err)
}
if want, got := 0, len(reader.Backlog); want != got {
t.Fatalf("expected nothing to be logged, got %+v", reader.Backlog)
}
tree.SetVerbosity("main", 10)
tree.MustLeveledFor("main").V(10).Info("this should get logged")
reader, err = tree.Read("", WithBacklog(BacklogAllAvailable), WithChildren())
if err != nil {
t.Fatalf("Read: %v", err)
}
if want, got := 1, len(reader.Backlog); want != got {
t.Fatalf("expected %d entries to get logged, got %d", want, got)
}
}
func TestMetadata(t *testing.T) {
tree := New()
tree.MustLeveledFor("main").Error("i am an error")
tree.MustLeveledFor("main").Warning("i am a warning")
tree.MustLeveledFor("main").Info("i am informative")
tree.MustLeveledFor("main").V(0).Info("i am a zero-level debug")
reader, err := tree.Read("", WithChildren(), WithBacklog(BacklogAllAvailable))
if err != nil {
t.Fatalf("Read: %v", err)
}
if want, got := 4, len(reader.Backlog); want != got {
t.Fatalf("expected %d entries, got %d", want, got)
}
for _, te := range []struct {
ix int
severity Severity
message string
}{
{0, ERROR, "i am an error"},
{1, WARNING, "i am a warning"},
{2, INFO, "i am informative"},
{3, INFO, "i am a zero-level debug"},
} {
p := reader.Backlog[te.ix]
if want, got := te.severity, p.Leveled.Severity(); want != got {
t.Errorf("wanted element %d to have severity %s, got %s", te.ix, want, got)
}
if want, got := te.message, p.Leveled.MessagesJoined(); want != got {
t.Errorf("wanted element %d to have message %q, got %q", te.ix, want, got)
}
if want, got := "logtree_test.go", strings.Split(p.Leveled.Location(), ":")[0]; want != got {
t.Errorf("wanted element %d to have file %q, got %q", te.ix, want, got)
}
}
}
func TestSeverity(t *testing.T) {
tree := New()
tree.MustLeveledFor("main").Error("i am an error")
tree.MustLeveledFor("main").Warning("i am a warning")
tree.MustLeveledFor("main").Info("i am informative")
tree.MustLeveledFor("main").V(0).Info("i am a zero-level debug")
reader, err := tree.Read("main", WithBacklog(BacklogAllAvailable), LeveledWithMinimumSeverity(WARNING))
if err != nil {
t.Fatalf("Read: %v", err)
}
if want, got := 2, len(reader.Backlog); want != got {
t.Fatalf("wanted %d entries, got %d", want, got)
}
if want, got := "i am an error", reader.Backlog[0].Leveled.MessagesJoined(); want != got {
t.Fatalf("wanted entry %q, got %q", want, got)
}
if want, got := "i am a warning", reader.Backlog[1].Leveled.MessagesJoined(); want != got {
t.Fatalf("wanted entry %q, got %q", want, got)
}
}
func TestAddedStackDepth(t *testing.T) {
tree := New()
helper := func(msg string) {
tree.MustLeveledFor("main").WithAddedStackDepth(1).Infof("oh no: %s", msg)
}
// The next three lines are tested to be next to each other.
helper("it failed")
tree.MustLeveledFor("main").Infof("something else")
reader, err := tree.Read("main", WithBacklog(BacklogAllAvailable))
if err != nil {
t.Fatalf("Read: %v", err)
}
if want, got := 2, len(reader.Backlog); want != got {
t.Fatalf("wanted %d entries, got %d", want, got)
}
if want, got := "oh no: it failed", reader.Backlog[0].Leveled.MessagesJoined(); want != got {
t.Errorf("wanted entry %q, got %q", want, got)
}
if want, got := "something else", reader.Backlog[1].Leveled.MessagesJoined(); want != got {
t.Errorf("wanted entry %q, got %q", want, got)
}
if first, second := reader.Backlog[0].Leveled.line, reader.Backlog[1].Leveled.line; first+1 != second {
t.Errorf("first entry at %d, second at %d, wanted one after the other", first, second)
}
}