vitess.io/vitess@v0.16.2/go/streamlog/streamlog_flaky_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package streamlog 18 19 import ( 20 "bufio" 21 "fmt" 22 "io" 23 "net" 24 "net/http" 25 "net/url" 26 "os" 27 "path" 28 "syscall" 29 "testing" 30 "time" 31 ) 32 33 type logMessage struct { 34 val string 35 } 36 37 func (l *logMessage) Format(params url.Values) string { 38 return l.val + "\n" 39 } 40 41 func testLogf(w io.Writer, params url.Values, m any) error { 42 _, err := io.WriteString(w, m.(*logMessage).Format(params)) 43 return err 44 } 45 46 func TestHTTP(t *testing.T) { 47 l, err := net.Listen("tcp", "localhost:0") 48 if err != nil { 49 t.Fatal(err) 50 } 51 defer l.Close() 52 addr := l.Addr().String() 53 54 go http.Serve(l, nil) 55 56 logger := New("logger", 1) 57 logger.ServeLogs("/log", testLogf) 58 59 // This should not block - there are no subscribers yet. 60 logger.Send(&logMessage{"val1"}) 61 62 // Subscribe. 63 resp, err := http.Get(fmt.Sprintf("http://%s/log", addr)) 64 if err != nil { 65 t.Fatal(err) 66 } 67 defer func() { 68 if resp != nil { 69 resp.Body.Close() 70 } 71 }() 72 body := bufio.NewReader(resp.Body) 73 if sz := len(logger.subscribed); sz != 1 { 74 t.Errorf("want 1, got %d", sz) 75 } 76 77 // Send some messages. 78 for i := 0; i < 10; i++ { 79 msg := fmt.Sprint("msg", i) 80 logger.Send(&logMessage{msg}) 81 val, err := body.ReadString('\n') 82 if err != nil { 83 t.Fatal(err) 84 } 85 if i == 0 && val == "val1\n" { 86 // the value that was sent has been in the 87 // channels and was actually processed after the 88 // subscription took effect. This is fine. 89 val, err = body.ReadString('\n') 90 if err != nil { 91 t.Fatal(err) 92 } 93 } 94 if want := msg + "\n"; val != want { 95 t.Errorf("want %q, got %q", msg, val) 96 } 97 } 98 99 // Shutdown. 100 resp.Body.Close() 101 resp = nil 102 body = nil 103 104 // Due to multiple layers of buffering in http.Server, we must 105 // send multiple messages to detect the client has gone away. 106 // 4 seems to be a minimum, but doesn't always work. So 10 it is. 107 logger.mu.Lock() 108 if want, got := 1, len(logger.subscribed); want != got { 109 t.Errorf("len(logger.subscribed) = %v, want %v", got, want) 110 } 111 logger.mu.Unlock() 112 for i := 0; i < 10; i++ { 113 logger.Send(&logMessage{"val3"}) 114 // Allow time for propagation (loopback interface - expected to be fast). 115 time.Sleep(1 * time.Millisecond) 116 } 117 logger.mu.Lock() 118 if want, got := 0, len(logger.subscribed); want != got { 119 t.Errorf("len(logger.subscribed) = %v, want %v", got, want) 120 } 121 logger.mu.Unlock() 122 } 123 124 func TestChannel(t *testing.T) { 125 logger := New("logger", 1) 126 127 // Subscribe. 128 ch := logger.Subscribe("test") 129 defer func() { 130 if ch != nil { 131 logger.Unsubscribe(ch) 132 } 133 }() 134 if sz := len(logger.subscribed); sz != 1 { 135 t.Errorf("want 1, got %d", sz) 136 } 137 138 // Send/receive some messages, one at a time. 139 for i := 0; i < 10; i++ { 140 msg := fmt.Sprint("msg", i) 141 done := make(chan struct{}) 142 go func() { 143 if want, got := msg+"\n", (<-ch).(*logMessage).Format(nil); got != want { 144 t.Errorf("Unexpected message in log. got: %q, want: %q", got, want) 145 } 146 close(done) 147 }() 148 logger.Send(&logMessage{msg}) 149 <-done 150 } 151 152 // Send/receive many messages with asynchronous writer/reader. 153 want := []string{"msg0", "msg1", "msg2", "msg3", "msg4", "msg5"} 154 got := make([]string, 0, len(want)) 155 readDone := make(chan struct{}) 156 writeDone := make(chan struct{}) 157 go func() { 158 for { 159 select { 160 case msg := <-ch: 161 got = append(got, msg.(*logMessage).Format(nil)) 162 case <-writeDone: 163 close(readDone) 164 return 165 } 166 } 167 }() 168 for _, x := range want { 169 logger.Send(&logMessage{x}) 170 // Allow propagation delay (cpu/memory-bound - expected to be very fast). 171 time.Sleep(1 * time.Millisecond) 172 } 173 close(writeDone) 174 <-readDone 175 if len(got) != len(want) { 176 t.Errorf("Bad results length: got %d, want %d", len(got), len(want)) 177 } else { 178 for i := 0; i < len(want); i++ { 179 if want[i]+"\n" != got[i] { 180 t.Errorf("Unexpected result in log: got %q, want %q", got[i], want[i]+"\n") 181 } 182 } 183 } 184 185 // Shutdown. 186 logger.Unsubscribe(ch) 187 ch = nil 188 if sz := len(logger.subscribed); sz != 0 { 189 t.Errorf("want 0, got %d", sz) 190 } 191 } 192 193 func TestFile(t *testing.T) { 194 logger := New("logger", 10) 195 196 dir := t.TempDir() 197 198 logPath := path.Join(dir, "test.log") 199 logChan, err := logger.LogToFile(logPath, testLogf) 200 defer logger.Unsubscribe(logChan) 201 if err != nil { 202 t.Errorf("error enabling file logger: %v", err) 203 } 204 205 logger.Send(&logMessage{"test 1"}) 206 logger.Send(&logMessage{"test 2"}) 207 208 // Allow time for propagation 209 time.Sleep(10 * time.Millisecond) 210 211 want := "test 1\ntest 2\n" 212 contents, _ := os.ReadFile(logPath) 213 got := string(contents) 214 if want != string(got) { 215 t.Errorf("streamlog file: want %q got %q", want, got) 216 } 217 218 // Rename and send another log which should go to the renamed file 219 rotatedPath := path.Join(dir, "test.log.1") 220 os.Rename(logPath, rotatedPath) 221 222 logger.Send(&logMessage{"test 3"}) 223 time.Sleep(10 * time.Millisecond) 224 225 want = "test 1\ntest 2\ntest 3\n" 226 contents, _ = os.ReadFile(rotatedPath) 227 got = string(contents) 228 if want != string(got) { 229 t.Errorf("streamlog file: want %q got %q", want, got) 230 } 231 232 // Send the rotate signal which should reopen the original file path 233 // for new logs to go to 234 if err := syscall.Kill(syscall.Getpid(), syscall.SIGUSR2); err != nil { 235 t.Logf("failed to send streamlog rotate signal: %v", err) 236 } 237 time.Sleep(10 * time.Millisecond) 238 239 logger.Send(&logMessage{"test 4"}) 240 time.Sleep(10 * time.Millisecond) 241 242 want = "test 1\ntest 2\ntest 3\n" 243 contents, _ = os.ReadFile(rotatedPath) 244 got = string(contents) 245 if want != string(got) { 246 t.Errorf("streamlog file: want %q got %q", want, got) 247 } 248 249 want = "test 4\n" 250 contents, _ = os.ReadFile(logPath) 251 got = string(contents) 252 if want != string(got) { 253 t.Errorf("streamlog file: want %q got %q", want, got) 254 } 255 }