gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/binary/binary_test.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package binary
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/binary"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"reflect"
    24  	"strings"
    25  	"testing"
    26  )
    27  
    28  func newInt32(i int32) *int32 {
    29  	return &i
    30  }
    31  
    32  func TestSize(t *testing.T) {
    33  	if got, want := Size(uint32(10)), uintptr(4); got != want {
    34  		t.Errorf("Got = %d, want = %d", got, want)
    35  	}
    36  }
    37  
    38  func TestPanic(t *testing.T) {
    39  	tests := []struct {
    40  		name string
    41  		f    func([]byte, binary.ByteOrder, any)
    42  		data any
    43  		want string
    44  	}{
    45  		{"Unmarshal int", Unmarshal, 5, "invalid type: int"},
    46  		{"Unmarshal []int", Unmarshal, []int{5}, "invalid type: int"},
    47  		{"Marshal int", func(_ []byte, bo binary.ByteOrder, d any) { Marshal(nil, bo, d) }, 5, "invalid type: int"},
    48  		{"Marshal int[]", func(_ []byte, bo binary.ByteOrder, d any) { Marshal(nil, bo, d) }, []int{5}, "invalid type: int"},
    49  		{"Unmarshal short buffer", Unmarshal, newInt32(5), "runtime error: index out of range"},
    50  		{"Unmarshal long buffer", func(_ []byte, bo binary.ByteOrder, d any) { Unmarshal(make([]byte, 50), bo, d) }, newInt32(5), "buffer too long by 46 bytes"},
    51  		{"marshal int", func(_ []byte, bo binary.ByteOrder, d any) { marshal(nil, bo, reflect.ValueOf(d)) }, 5, "invalid type: int"},
    52  		{"Size int", func(_ []byte, _ binary.ByteOrder, d any) { Size(d) }, 5, "invalid type: int"},
    53  	}
    54  
    55  	for _, test := range tests {
    56  		t.Run(test.name, func(t *testing.T) {
    57  			defer func() {
    58  				r := recover()
    59  				if got := fmt.Sprint(r); !strings.HasPrefix(got, test.want) {
    60  					t.Errorf("Got recover() = %q, want prefix = %q", got, test.want)
    61  				}
    62  			}()
    63  
    64  			test.f(nil, LittleEndian, test.data)
    65  		})
    66  	}
    67  }
    68  
    69  type inner struct {
    70  	Field int32
    71  }
    72  
    73  type outer struct {
    74  	Int8   int8
    75  	Int16  int16
    76  	Int32  int32
    77  	Int64  int64
    78  	Uint8  uint8
    79  	Uint16 uint16
    80  	Uint32 uint32
    81  	Uint64 uint64
    82  
    83  	Slice  []int32
    84  	Array  [5]int32
    85  	Struct inner
    86  }
    87  
    88  func TestMarshalUnmarshal(t *testing.T) {
    89  	want := outer{
    90  		1, 2, 3, 4, 5, 6, 7, 8,
    91  		[]int32{9, 10, 11},
    92  		[5]int32{12, 13, 14, 15, 16},
    93  		inner{17},
    94  	}
    95  	buf := Marshal(nil, LittleEndian, want)
    96  	got := outer{Slice: []int32{0, 0, 0}}
    97  	Unmarshal(buf, LittleEndian, &got)
    98  	if !reflect.DeepEqual(&got, &want) {
    99  		t.Errorf("Got = %#v, want = %#v", got, want)
   100  	}
   101  }
   102  
   103  type outerBenchmark struct {
   104  	Int8   int8
   105  	Int16  int16
   106  	Int32  int32
   107  	Int64  int64
   108  	Uint8  uint8
   109  	Uint16 uint16
   110  	Uint32 uint32
   111  	Uint64 uint64
   112  
   113  	Array  [5]int32
   114  	Struct inner
   115  }
   116  
   117  func BenchmarkMarshalUnmarshal(b *testing.B) {
   118  	b.ReportAllocs()
   119  
   120  	in := outerBenchmark{
   121  		1, 2, 3, 4, 5, 6, 7, 8,
   122  		[5]int32{9, 10, 11, 12, 13},
   123  		inner{14},
   124  	}
   125  	buf := make([]byte, Size(&in))
   126  	out := outerBenchmark{}
   127  
   128  	for i := 0; i < b.N; i++ {
   129  		buf := Marshal(buf[:0], LittleEndian, &in)
   130  		Unmarshal(buf, LittleEndian, &out)
   131  	}
   132  }
   133  
   134  func BenchmarkReadWrite(b *testing.B) {
   135  	b.ReportAllocs()
   136  
   137  	in := outerBenchmark{
   138  		1, 2, 3, 4, 5, 6, 7, 8,
   139  		[5]int32{9, 10, 11, 12, 13},
   140  		inner{14},
   141  	}
   142  	buf := bytes.NewBuffer(make([]byte, binary.Size(&in)))
   143  	out := outerBenchmark{}
   144  
   145  	for i := 0; i < b.N; i++ {
   146  		buf.Reset()
   147  		if err := binary.Write(buf, LittleEndian, &in); err != nil {
   148  			b.Error("Write:", err)
   149  		}
   150  		if err := binary.Read(buf, LittleEndian, &out); err != nil {
   151  			b.Error("Read:", err)
   152  		}
   153  	}
   154  }
   155  
   156  type outerPadding struct {
   157  	_ int8
   158  	_ int16
   159  	_ int32
   160  	_ int64
   161  	_ uint8
   162  	_ uint16
   163  	_ uint32
   164  	_ uint64
   165  
   166  	_ []int32
   167  	_ [5]int32
   168  	_ inner
   169  }
   170  
   171  func TestMarshalUnmarshalPadding(t *testing.T) {
   172  	var want outerPadding
   173  	buf := Marshal(nil, LittleEndian, want)
   174  	var got outerPadding
   175  	Unmarshal(buf, LittleEndian, &got)
   176  	if !reflect.DeepEqual(&got, &want) {
   177  		t.Errorf("Got = %#v, want = %#v", got, want)
   178  	}
   179  }
   180  
   181  // Numbers with bits in every byte that distinguishable in big and little endian.
   182  const (
   183  	want16 = 64<<8 | 128
   184  	want32 = 16<<24 | 32<<16 | want16
   185  	want64 = 1<<56 | 2<<48 | 4<<40 | 8<<32 | want32
   186  )
   187  
   188  func TestReadWriteUint16(t *testing.T) {
   189  	const want = uint16(want16)
   190  	var buf bytes.Buffer
   191  	if err := WriteUint16(&buf, LittleEndian, want); err != nil {
   192  		t.Error("WriteUint16:", err)
   193  	}
   194  	got, err := ReadUint16(&buf, LittleEndian)
   195  	if err != nil {
   196  		t.Error("ReadUint16:", err)
   197  	}
   198  	if got != want {
   199  		t.Errorf("got = %d, want = %d", got, want)
   200  	}
   201  }
   202  
   203  func TestReadWriteUint32(t *testing.T) {
   204  	const want = uint32(want32)
   205  	var buf bytes.Buffer
   206  	if err := WriteUint32(&buf, LittleEndian, want); err != nil {
   207  		t.Error("WriteUint32:", err)
   208  	}
   209  	got, err := ReadUint32(&buf, LittleEndian)
   210  	if err != nil {
   211  		t.Error("ReadUint32:", err)
   212  	}
   213  	if got != want {
   214  		t.Errorf("got = %d, want = %d", got, want)
   215  	}
   216  }
   217  
   218  func TestReadWriteUint64(t *testing.T) {
   219  	const want = uint64(want64)
   220  	var buf bytes.Buffer
   221  	if err := WriteUint64(&buf, LittleEndian, want); err != nil {
   222  		t.Error("WriteUint64:", err)
   223  	}
   224  	got, err := ReadUint64(&buf, LittleEndian)
   225  	if err != nil {
   226  		t.Error("ReadUint64:", err)
   227  	}
   228  	if got != want {
   229  		t.Errorf("got = %d, want = %d", got, want)
   230  	}
   231  }
   232  
   233  type readWriter struct {
   234  	err error
   235  }
   236  
   237  func (rw *readWriter) Write([]byte) (int, error) {
   238  	return 0, rw.err
   239  }
   240  
   241  func (rw *readWriter) Read([]byte) (int, error) {
   242  	return 0, rw.err
   243  }
   244  
   245  func TestReadWriteError(t *testing.T) {
   246  	tests := []struct {
   247  		name string
   248  		f    func(rw io.ReadWriter) error
   249  	}{
   250  		{"WriteUint16", func(rw io.ReadWriter) error { return WriteUint16(rw, LittleEndian, 0) }},
   251  		{"ReadUint16", func(rw io.ReadWriter) error { _, err := ReadUint16(rw, LittleEndian); return err }},
   252  		{"WriteUint32", func(rw io.ReadWriter) error { return WriteUint32(rw, LittleEndian, 0) }},
   253  		{"ReadUint32", func(rw io.ReadWriter) error { _, err := ReadUint32(rw, LittleEndian); return err }},
   254  		{"WriteUint64", func(rw io.ReadWriter) error { return WriteUint64(rw, LittleEndian, 0) }},
   255  		{"ReadUint64", func(rw io.ReadWriter) error { _, err := ReadUint64(rw, LittleEndian); return err }},
   256  	}
   257  
   258  	for _, test := range tests {
   259  		t.Run(test.name, func(t *testing.T) {
   260  			want := errors.New("want")
   261  			if got := test.f(&readWriter{want}); got != want {
   262  				t.Errorf("got = %v, want = %v", got, want)
   263  			}
   264  		})
   265  	}
   266  }