github.com/kubri/kubri@v0.5.1-0.20240317001612-bda2aaef967e/integrations/apt/deb/encode_test.go (about)

     1  package deb_test
     2  
     3  import (
     4  	"errors"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/google/go-cmp/cmp"
     9  	"github.com/google/go-cmp/cmp/cmpopts"
    10  
    11  	"github.com/kubri/kubri/integrations/apt/deb"
    12  	"github.com/kubri/kubri/internal/test"
    13  )
    14  
    15  func TestMarshal(t *testing.T) {
    16  	in := record{
    17  		String:    "test",
    18  		Hex:       [4]byte{1, 2, 3, 4},
    19  		Int:       1,
    20  		Int8:      1,
    21  		Int16:     1,
    22  		Int32:     1,
    23  		Uint:      1,
    24  		Uint8:     1,
    25  		Uint16:    1,
    26  		Uint32:    1,
    27  		Float32:   1.123,
    28  		Float64:   1.123,
    29  		Stringer:  stringer{"test"},
    30  		Marshaler: &marshaler{"test"},
    31  		Date:      time.Date(2023, 1, 10, 19, 4, 25, 0, time.UTC),
    32  	}
    33  
    34  	want := `String: test
    35  Hex: 01020304
    36  Int: 1
    37  Int8: 1
    38  Int16: 1
    39  Int32: 1
    40  Uint: 1
    41  Uint8: 1
    42  Uint16: 1
    43  Uint32: 1
    44  Float32: 1.123
    45  Float64: 1.123
    46  Stringer: test
    47  Marshaler: test
    48  Date: Tue, 10 Jan 2023 19:04:25 UTC
    49  `
    50  
    51  	tests := []struct {
    52  		msg  string
    53  		in   any
    54  		want string
    55  	}{
    56  		{
    57  			msg:  "struct",
    58  			in:   in,
    59  			want: want,
    60  		},
    61  		{
    62  			msg:  "struct pointer",
    63  			in:   &in,
    64  			want: want,
    65  		},
    66  		{
    67  			msg:  "struct slice",
    68  			in:   []record{in, in},
    69  			want: want + "\n" + want,
    70  		},
    71  		{
    72  			msg:  "struct pointer slice",
    73  			in:   []*record{&in, &in},
    74  			want: want + "\n" + want,
    75  		},
    76  		{
    77  			msg: "multi-line text",
    78  			in: record{
    79  				String:    "foo\nbar\nbaz\n\nfoobar",
    80  				Stringer:  stringer{"foo\nbar\nbaz\n\nfoobar"},
    81  				Marshaler: &marshaler{"foo\nbar\nbaz\n\nfoobar"},
    82  			},
    83  			want: `String: foo
    84   bar
    85   baz
    86   .
    87   foobar
    88  Stringer: foo
    89   bar
    90   baz
    91   .
    92   foobar
    93  Marshaler: foo
    94   bar
    95   baz
    96   .
    97   foobar
    98  `,
    99  		},
   100  		{
   101  			msg: "multi-line text starting with empty line",
   102  			in: record{
   103  				String:    "\nfoo\nbar",
   104  				Stringer:  stringer{"\nfoo\nbar"},
   105  				Marshaler: &marshaler{"\nfoo\nbar"},
   106  			},
   107  			want: `String:
   108   foo
   109   bar
   110  Stringer:
   111   foo
   112   bar
   113  Marshaler:
   114   foo
   115   bar
   116  `,
   117  		},
   118  		{
   119  			msg: "nil values",
   120  			in: struct {
   121  				Date      *time.Time
   122  				Marshaler *marshaler
   123  				Stringer  *stringer
   124  				String    *string
   125  				Int       *int
   126  				Uint      *uint
   127  				Float     *float64
   128  			}{},
   129  			want: "",
   130  		},
   131  		{
   132  			msg: "zero values",
   133  			in: struct {
   134  				Marshaler *marshaler
   135  				Stringer  *stringer
   136  				String    *string
   137  			}{
   138  				Marshaler: &marshaler{},
   139  				Stringer:  &stringer{},
   140  				String:    new(string),
   141  			},
   142  			want: "",
   143  		},
   144  		{
   145  			msg: "unexported/ignored fields",
   146  			in: struct {
   147  				unexported string
   148  				Ignored    string `deb:"-"`
   149  				Test       string
   150  			}{unexported: "foo", Ignored: "bar", Test: "baz"},
   151  			want: "Test: baz\n",
   152  		},
   153  		{
   154  			msg: "named fields",
   155  			in: struct {
   156  				Name string `deb:"Alias"` //nolint:tagliatelle
   157  			}{Name: "foo"},
   158  			want: "Alias: foo\n",
   159  		},
   160  	}
   161  
   162  	for _, test := range tests {
   163  		b, err := deb.Marshal(test.in)
   164  		if err != nil {
   165  			t.Error(test.msg, err)
   166  		}
   167  
   168  		if diff := cmp.Diff(test.want, string(b)); diff != "" {
   169  			t.Errorf("%s:\n%s", test.msg, diff)
   170  		}
   171  	}
   172  }
   173  
   174  func TestEncodeErrors(t *testing.T) {
   175  	tests := []struct {
   176  		msg   string
   177  		value any
   178  		err   string
   179  	}{
   180  		{
   181  			msg:   "nil",
   182  			value: nil,
   183  			err:   "unsupported type: nil",
   184  		},
   185  		{
   186  			msg:   "unsupported type",
   187  			value: &[]struct{ V complex128 }{},
   188  			err:   "unsupported type: complex128",
   189  		},
   190  		{
   191  			msg:   "marshaler error",
   192  			value: &[]struct{ V *errMarshaler }{{V: &errMarshaler{errors.New("marshal error")}}},
   193  			err:   "marshal error",
   194  		},
   195  	}
   196  
   197  	opts := test.CompareErrorMessages()
   198  
   199  	for _, test := range tests {
   200  		_, err := deb.Marshal(test.value)
   201  		if diff := cmp.Diff(errors.New(test.err), err, opts); diff != "" {
   202  			t.Errorf("%s returned unexpected error:\n%s", test.msg, diff)
   203  		}
   204  	}
   205  
   206  	// Ensure write errors are passed though from each part of the application.
   207  	// Test up to 6 writes: field name, colon, space, value, newline (field end), newline (slice element end)
   208  	for i := 1; i <= 6; i++ {
   209  		want := errors.New("custom error")
   210  		w := &errWriter{i, want}
   211  		err := deb.NewEncoder(w).Encode([]record{{String: "foo"}, {String: "bar"}})
   212  		if diff := cmp.Diff(err, want, cmpopts.EquateErrors()); diff != "" {
   213  			t.Errorf("write %d should return error:\n%s", i, diff)
   214  		}
   215  	}
   216  }
   217  
   218  func BenchmarkMarshal(b *testing.B) {
   219  	type record struct {
   220  		String string
   221  		Hex    [4]byte
   222  		Int    int
   223  	}
   224  
   225  	v := []record{
   226  		{
   227  			String: "foo\nbar\nbaz",
   228  			Hex:    [4]byte{1, 2, 3, 4},
   229  			Int:    1,
   230  		},
   231  		{
   232  			String: "foo\nbar\nbaz",
   233  			Hex:    [4]byte{1, 2, 3, 4},
   234  			Int:    1,
   235  		},
   236  	}
   237  
   238  	for i := 0; i < b.N; i++ {
   239  		deb.Marshal(v)
   240  	}
   241  }
   242  
   243  type errWriter struct {
   244  	n int
   245  	e error
   246  }
   247  
   248  func (w *errWriter) Write(p []byte) (int, error) {
   249  	w.n--
   250  	if w.n == 0 {
   251  		return 0, w.e
   252  	}
   253  	return len(p), nil
   254  }