github.com/ethersphere/bee/v2@v2.2.0/pkg/p2p/protobuf/protobuf_test.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package protobuf_test 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "io" 12 "testing" 13 "time" 14 15 "github.com/ethersphere/bee/v2/pkg/p2p" 16 "github.com/ethersphere/bee/v2/pkg/p2p/protobuf" 17 "github.com/ethersphere/bee/v2/pkg/p2p/protobuf/internal/pb" 18 ) 19 20 func TestReader_ReadMsg(t *testing.T) { 21 t.Parallel() 22 23 messages := []string{"first", "second", "third"} 24 25 for _, tc := range []struct { 26 name string 27 readerFunc func() protobuf.Reader 28 }{ 29 { 30 name: "NewReader", 31 readerFunc: func() protobuf.Reader { 32 return protobuf.NewReader(newMessageReader(messages, 0)) 33 }, 34 }, 35 { 36 name: "NewWriterAndReader", 37 readerFunc: func() protobuf.Reader { 38 _, r := protobuf.NewWriterAndReader( 39 newNoopWriteCloser( 40 newMessageReader(messages, 0), 41 ), 42 ) 43 return r 44 }, 45 }, 46 } { 47 tc := tc 48 t.Run(tc.name, func(t *testing.T) { 49 t.Parallel() 50 51 r := tc.readerFunc() 52 var msg pb.Message 53 for i := 0; i < len(messages); i++ { 54 err := r.ReadMsg(&msg) 55 if i == len(messages) { 56 if !errors.Is(err, io.EOF) { 57 t.Fatalf("got error %v, want %v", err, io.EOF) 58 } 59 break 60 } 61 if err != nil { 62 t.Fatal(err) 63 } 64 want := messages[i] 65 got := msg.Text 66 if got != want { 67 t.Errorf("got message %q, want %q", got, want) 68 } 69 } 70 }) 71 } 72 } 73 74 func TestReader_timeout(t *testing.T) { 75 t.Parallel() 76 77 messages := []string{"first", "second", "third"} 78 79 for _, tc := range []struct { 80 name string 81 readerFunc func() protobuf.Reader 82 }{ 83 { 84 name: "NewReader", 85 readerFunc: func() protobuf.Reader { 86 return protobuf.NewReader(newMessageReader(messages, 500*time.Millisecond)) 87 }, 88 }, 89 { 90 name: "NewWriterAndReader", 91 readerFunc: func() protobuf.Reader { 92 _, r := protobuf.NewWriterAndReader( 93 newNoopWriteCloser( 94 newMessageReader(messages, 500*time.Millisecond), 95 ), 96 ) 97 return r 98 }, 99 }, 100 } { 101 tc := tc 102 t.Run(tc.name, func(t *testing.T) { 103 t.Parallel() 104 105 t.Run("WithContext", func(t *testing.T) { 106 t.Parallel() 107 108 r := tc.readerFunc() 109 var msg pb.Message 110 for i := 0; i < len(messages); i++ { 111 var timeout time.Duration 112 if i == 0 { 113 timeout = 1000 * time.Millisecond 114 } else { 115 timeout = 10 * time.Millisecond 116 } 117 ctx, cancel := context.WithTimeout(context.Background(), timeout) 118 defer cancel() 119 err := r.ReadMsgWithContext(ctx, &msg) 120 if i == 0 { 121 if err != nil { 122 t.Parallel() 123 t.Fatal(err) 124 } 125 } else { 126 if !errors.Is(err, context.DeadlineExceeded) { 127 t.Fatalf("got error %v, want %v", err, context.DeadlineExceeded) 128 } 129 break 130 } 131 want := messages[i] 132 got := msg.Text 133 if got != want { 134 t.Errorf("got message %q, want %q", got, want) 135 } 136 } 137 }) 138 }) 139 } 140 } 141 142 func TestWriter(t *testing.T) { 143 t.Parallel() 144 145 messages := []string{"first", "second", "third"} 146 147 for _, tc := range []struct { 148 name string 149 writerFunc func() (protobuf.Writer, <-chan string) 150 }{ 151 { 152 name: "NewWriter", 153 writerFunc: func() (protobuf.Writer, <-chan string) { 154 w, msgs := newMessageWriter(0) 155 return protobuf.NewWriter(w), msgs 156 }, 157 }, 158 { 159 name: "NewWriterAndReader", 160 writerFunc: func() (protobuf.Writer, <-chan string) { 161 w, msgs := newMessageWriter(0) 162 writer, _ := protobuf.NewWriterAndReader(newNoopReadCloser(w)) 163 return writer, msgs 164 }, 165 }, 166 } { 167 tc := tc 168 t.Run(tc.name, func(t *testing.T) { 169 t.Parallel() 170 171 w, msgs := tc.writerFunc() 172 173 for _, m := range messages { 174 if err := w.WriteMsg(&pb.Message{ 175 Text: m, 176 }); err != nil { 177 t.Fatal(err) 178 } 179 180 if got := <-msgs; got != m { 181 t.Fatalf("got message %q, want %q", got, m) 182 } 183 } 184 }) 185 } 186 } 187 188 func TestWriter_timeout(t *testing.T) { 189 t.Parallel() 190 191 messages := []string{"first", "second", "third"} 192 193 for _, tc := range []struct { 194 name string 195 writerFunc func() (protobuf.Writer, <-chan string) 196 }{ 197 { 198 name: "NewWriter", 199 writerFunc: func() (protobuf.Writer, <-chan string) { 200 w, msgs := newMessageWriter(500 * time.Millisecond) 201 return protobuf.NewWriter(w), msgs 202 }, 203 }, 204 { 205 name: "NewWriterAndReader", 206 writerFunc: func() (protobuf.Writer, <-chan string) { 207 w, msgs := newMessageWriter(500 * time.Millisecond) 208 writer, _ := protobuf.NewWriterAndReader(newNoopReadCloser(w)) 209 return writer, msgs 210 }, 211 }, 212 } { 213 tc := tc 214 t.Run(tc.name+"WithContext", func(t *testing.T) { 215 t.Parallel() 216 217 w, msgs := tc.writerFunc() 218 219 for i, m := range messages { 220 var timeout time.Duration 221 if i == 0 { 222 timeout = 1000 * time.Millisecond 223 } else { 224 timeout = 10 * time.Millisecond 225 } 226 ctx, cancel := context.WithTimeout(context.Background(), timeout) 227 defer cancel() 228 err := w.WriteMsgWithContext(ctx, &pb.Message{ 229 Text: m, 230 }) 231 if i == 0 { 232 if err != nil { 233 t.Fatal(err) 234 } 235 } else { 236 if !errors.Is(err, context.DeadlineExceeded) { 237 t.Fatalf("got error %v, want %v", err, context.DeadlineExceeded) 238 } 239 break 240 } 241 if got := <-msgs; got != m { 242 t.Fatalf("got message %q, want %q", got, m) 243 } 244 } 245 }) 246 } 247 } 248 249 func TestReadMessages(t *testing.T) { 250 t.Parallel() 251 252 messages := []string{"first", "second", "third"} 253 254 r := newMessageReader(messages, 0) 255 256 got, err := protobuf.ReadMessages(r, func() protobuf.Message { return new(pb.Message) }) 257 if err != nil { 258 t.Fatal(err) 259 } 260 261 gotMessages := make([]string, 0, len(got)) 262 for _, m := range got { 263 gotMessages = append(gotMessages, m.(*pb.Message).Text) 264 } 265 266 if fmt.Sprint(gotMessages) != fmt.Sprint(messages) { 267 t.Errorf("got messages %v, want %v", gotMessages, messages) 268 } 269 } 270 271 func newMessageReader(messages []string, delay time.Duration) io.Reader { 272 r, pipe := io.Pipe() 273 w := protobuf.NewWriter(pipe) 274 275 go func() { 276 for _, m := range messages { 277 if err := w.WriteMsg(&pb.Message{ 278 Text: m, 279 }); err != nil { 280 panic(err) 281 } 282 } 283 if err := pipe.Close(); err != nil { 284 panic(err) 285 } 286 }() 287 288 return delayedReader{r: r, delay: delay} 289 } 290 291 func newMessageWriter(delay time.Duration) (w io.Writer, messages <-chan string) { 292 pipe, w := io.Pipe() 293 r := protobuf.NewReader(pipe) 294 msgs := make(chan string) 295 296 go func() { 297 defer close(msgs) 298 299 var msg pb.Message 300 for { 301 err := r.ReadMsg(&msg) 302 if err != nil { 303 if errors.Is(err, io.EOF) { 304 return 305 } 306 panic(err) 307 } 308 msgs <- msg.Text 309 } 310 }() 311 return delayedWriter{w: w, delay: delay}, msgs 312 } 313 314 type delayedWriter struct { 315 w io.Writer 316 delay time.Duration 317 } 318 319 func (d delayedWriter) Write(p []byte) (n int, err error) { 320 time.Sleep(d.delay) 321 return d.w.Write(p) 322 } 323 324 type delayedReader struct { 325 r io.Reader 326 delay time.Duration 327 } 328 329 func (d delayedReader) Read(p []byte) (n int, err error) { 330 time.Sleep(d.delay) 331 return d.r.Read(p) 332 } 333 334 type noopWriteCloser struct { 335 io.Reader 336 } 337 338 func newNoopWriteCloser(r io.Reader) noopWriteCloser { 339 return noopWriteCloser{Reader: r} 340 } 341 342 func (noopWriteCloser) Write(p []byte) (n int, err error) { 343 return 0, nil 344 } 345 346 func (noopWriteCloser) Headers() p2p.Headers { 347 return nil 348 } 349 350 func (noopWriteCloser) ResponseHeaders() p2p.Headers { 351 return nil 352 } 353 354 func (noopWriteCloser) Close() error { 355 return nil 356 } 357 358 func (noopWriteCloser) FullClose() error { 359 return nil 360 } 361 362 func (noopWriteCloser) Reset() error { 363 return nil 364 } 365 366 type noopReadCloser struct { 367 io.Writer 368 } 369 370 func newNoopReadCloser(w io.Writer) noopReadCloser { 371 return noopReadCloser{Writer: w} 372 } 373 374 func (noopReadCloser) Read(p []byte) (n int, err error) { 375 return 0, nil 376 } 377 378 func (noopReadCloser) Headers() p2p.Headers { 379 return nil 380 } 381 382 func (noopReadCloser) ResponseHeaders() p2p.Headers { 383 return nil 384 } 385 386 func (noopReadCloser) Close() error { 387 return nil 388 } 389 390 func (noopReadCloser) FullClose() error { 391 return nil 392 } 393 394 func (noopReadCloser) Reset() error { 395 return nil 396 }