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  }