blake.io/pqx@v0.2.2-0.20231231055241-83f2254c0a07/internal/logplex/logplex_test.go (about) 1 package logplex 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "runtime" 8 "strings" 9 "sync" 10 "testing" 11 12 "kr.dev/diff" 13 ) 14 15 func testSplitter(line []byte) (key, msg []byte) { 16 key, msg, hasSep := bytes.Cut(line, []byte("::")) 17 if hasSep { 18 return key, msg 19 } 20 return nil, line 21 } 22 23 func TestLogplex(t *testing.T) { 24 var ( 25 d0 strings.Builder 26 d1 strings.Builder 27 d2 strings.Builder 28 d3 strings.Builder 29 ) 30 31 lp := &Logplex{ 32 Sink: &d0, 33 Split: testSplitter, 34 } 35 36 write := func(s string) { 37 t.Helper() 38 n, err := lp.Write([]byte(s)) 39 if err != nil { 40 t.Error(err) 41 } 42 if n != len(s) { 43 t.Errorf("wrote %d bytes, want %d", n, len(s)) 44 } 45 } 46 47 write("nothing\n") //nolint 48 diff.Test(t, t.Errorf, d0.String(), "nothing\n") 49 d0.Reset() 50 51 lp.Watch("d1", &d1) 52 lp.Watch("d2", &d2) 53 54 // unknowns sent to Sink 55 write("zero\n") 56 write("d0::zero\n") // unknown prefix; sent to Drain 57 58 // d1 59 write("d1::one\n") 60 write("d1::a") 61 write("b\n") 62 63 // d2 64 write("d2::t") 65 write("wo\n") 66 67 // d3 68 write("d3::three\n") // write d3 before Watch 69 70 lp.Watch("d3", &d3) // late watch 71 write("d3:") // split seperator 72 write(":3\n\tcontinuation\n") 73 74 // detach d1 so it goes to Sink 75 lp.Unwatch("d1") 76 write("d1::detached\n") 77 78 diff.Test(t, t.Errorf, d0.String(), "zero\nd0::zero\nd3::three\nd1::detached\n") // captures d3 logs until Watch("d3", ...) 79 diff.Test(t, t.Errorf, d1.String(), "one\nab\n") 80 diff.Test(t, t.Errorf, d2.String(), "two\n") 81 diff.Test(t, t.Errorf, d3.String(), "3\n\tcontinuation\n") 82 } 83 84 // run with -race 85 func TestConcurrency(t *testing.T) { 86 var ( 87 d0 strings.Builder 88 d1 strings.Builder 89 d2 strings.Builder 90 ) 91 92 lp := &Logplex{ 93 Sink: &d0, 94 } 95 lp.Watch("d1", &d1) 96 lp.Watch("d2", &d2) 97 98 const seq = "abcdefghijklmnopqrstuvwxyz" 99 var g sync.WaitGroup 100 for i := 0; i < runtime.GOMAXPROCS(0); i++ { 101 i := i 102 g.Add(1) 103 go func() { 104 defer g.Done() 105 for _, c := range seq { 106 fmt.Fprintf(lp, "d%d::%c\n", i, c) 107 } 108 }() 109 } 110 111 g.Wait() 112 } 113 114 func TestWriteAllocs(t *testing.T) { 115 lp := &Logplex{ 116 Sink: io.Discard, 117 Split: testSplitter, 118 } 119 lp.Watch("a", io.Discard) 120 121 line := []byte("a::b\n") 122 got := testing.AllocsPerRun(100, func() { 123 lp.Write(line) //nolint 124 }) 125 126 if got > 0 { 127 t.Errorf("got %f allocs, want 0", got) 128 } 129 } 130 131 func BenchmarkWrite(b *testing.B) { 132 b.ReportAllocs() 133 134 lp := &Logplex{ 135 Sink: io.Discard, 136 Split: testSplitter, 137 } 138 lp.Watch("a", io.Discard) 139 140 line := []byte("a::b\n") 141 for i := 0; i < b.N; i++ { 142 lp.Write(line) //nolint 143 } 144 }