github.com/abemedia/appcast@v0.4.0/integrations/apt/deb/decode_test.go (about)

     1  package deb_test
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"reflect"
     7  	"strings"
     8  	"testing"
     9  	"testing/iotest"
    10  	"time"
    11  
    12  	"github.com/abemedia/appcast/integrations/apt/deb"
    13  	"github.com/abemedia/appcast/internal/test"
    14  	"github.com/google/go-cmp/cmp"
    15  )
    16  
    17  func TestUnmarshal(t *testing.T) {
    18  	in := `String: test
    19  Hex: 01020304
    20  Int: 1
    21  Int8: 1
    22  Int16: 1
    23  Int32: 1
    24  Uint: 1
    25  Uint8: 1
    26  Uint16: 1
    27  Uint32: 1
    28  Float32: 1.123
    29  Float64: 1.123
    30  Marshaler: test
    31  Date: Tue, 10 Jan 2023 19:04:25 UTC
    32  `
    33  
    34  	want := record{
    35  		String:    "test",
    36  		Hex:       [4]byte{1, 2, 3, 4},
    37  		Int:       1,
    38  		Int8:      1,
    39  		Int16:     1,
    40  		Int32:     1,
    41  		Uint:      1,
    42  		Uint8:     1,
    43  		Uint16:    1,
    44  		Uint32:    1,
    45  		Float32:   1.123,
    46  		Float64:   1.123,
    47  		Marshaler: &marshaler{"test"},
    48  		Date:      time.Date(2023, 1, 10, 19, 4, 25, 0, time.UTC),
    49  	}
    50  
    51  	tests := []struct {
    52  		msg  string
    53  		in   string
    54  		want any
    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:   in + "\r\n\r\n" + in,
    69  			want: []record{want, want},
    70  		},
    71  		{
    72  			msg:  "struct pointer slice",
    73  			in:   in + "\n" + in,
    74  			want: []*record{&want, &want},
    75  		},
    76  		{
    77  			msg: "multi-line text",
    78  			in: `String: foo
    79   bar
    80   baz
    81   .
    82   foobar
    83  Marshaler: foo
    84   bar
    85   baz
    86   .
    87   foobar
    88  `,
    89  			want: record{
    90  				String:    "foo\nbar\nbaz\n\nfoobar",
    91  				Marshaler: &marshaler{"foo\nbar\nbaz\n\nfoobar"},
    92  			},
    93  		},
    94  		{
    95  			msg: "multi-line text starting with empty line",
    96  			in: `String:
    97   foo
    98   bar
    99  Marshaler:
   100   foo
   101   bar
   102  `,
   103  			want: record{
   104  				String:    "\nfoo\nbar",
   105  				Marshaler: &marshaler{"\nfoo\nbar"},
   106  			},
   107  		},
   108  		{
   109  			msg: "empty values",
   110  			in: `String: 
   111  Hex: 
   112  Int: 
   113  Int8: 
   114  Int16: 
   115  Int32: 
   116  Uint: 
   117  Uint8: 
   118  Uint16: 
   119  Uint32: 
   120  Float32: 
   121  Float64: 
   122  Marshaler: 
   123  Date: 
   124  `,
   125  			want: record{},
   126  		},
   127  		{
   128  			msg: "unknown keys",
   129  			in: `Foo: bar
   130  String: test
   131  `,
   132  			want: record{
   133  				String: "test",
   134  			},
   135  		},
   136  		{
   137  			msg: "non-pointer unmarshaler",
   138  			in: `Marshaler: test
   139  `,
   140  			want: struct{ Marshaler marshaler }{
   141  				Marshaler: marshaler{"test"},
   142  			},
   143  		},
   144  		{
   145  			msg: "unexported/ignored fields",
   146  			in:  "unexported: foo\nIgnored: bar\nTest: baz\n",
   147  			want: struct {
   148  				unexported string
   149  				Ignored    string `deb:"-"`
   150  				Test       string
   151  			}{Test: "baz"},
   152  		},
   153  		{
   154  			msg: "named fields",
   155  			in:  "Alias: foo\n",
   156  			want: struct {
   157  				Name string `deb:"Alias"` //nolint:tagliatelle
   158  			}{Name: "foo"},
   159  		},
   160  	}
   161  
   162  	opts := test.ExportAll()
   163  
   164  	for _, test := range tests {
   165  		v := reflect.New(reflect.TypeOf(test.want))
   166  		if err := deb.Unmarshal([]byte(test.in), v.Interface()); err != nil {
   167  			t.Error(test.msg, err)
   168  		} else {
   169  			if diff := cmp.Diff(test.want, v.Elem().Interface(), opts); diff != "" {
   170  				t.Errorf("%s:\n%s", test.msg, diff)
   171  			}
   172  		}
   173  	}
   174  }
   175  
   176  func TestDecodeErrors(t *testing.T) {
   177  	tests := []struct {
   178  		msg    string
   179  		reader io.Reader
   180  		value  any
   181  		err    string
   182  	}{
   183  		{
   184  			msg:   "non-pointer",
   185  			value: record{},
   186  			err:   "must use pointer",
   187  		},
   188  		{
   189  			msg:    "struct reader error",
   190  			reader: iotest.ErrReader(errors.New("reader error")),
   191  			value:  &record{},
   192  			err:    "reader error",
   193  		},
   194  		{
   195  			msg:    "slice reader error",
   196  			reader: iotest.ErrReader(errors.New("reader error")),
   197  			value:  &[]record{{}},
   198  			err:    "reader error",
   199  		},
   200  		{
   201  			msg:   "nil",
   202  			value: nil,
   203  			err:   "unsupported type: nil",
   204  		},
   205  		{
   206  			msg:   "unsupported type",
   207  			value: &[]struct{ V complex128 }{},
   208  			err:   "unsupported type: complex128",
   209  		},
   210  		{
   211  			msg:    "invalid date",
   212  			reader: strings.NewReader("Date: test\n"),
   213  			value:  &[]record{},
   214  			err:    `parsing time "test" as "Mon, 02 Jan 2006 15:04:05 MST": cannot parse "test" as "Mon"`,
   215  		},
   216  		{
   217  			msg:    "invalid integer",
   218  			reader: strings.NewReader("Int: test\n"),
   219  			value:  &[]record{},
   220  			err:    `strconv.ParseInt: parsing "test": invalid syntax`,
   221  		},
   222  		{
   223  			msg:    "invalid unsigned integer",
   224  			reader: strings.NewReader("Uint: test\n"),
   225  			value:  &[]record{},
   226  			err:    `strconv.ParseUint: parsing "test": invalid syntax`,
   227  		},
   228  		{
   229  			msg:    "invalid float",
   230  			reader: strings.NewReader("Float64: test\n"),
   231  			value:  &[]record{},
   232  			err:    `strconv.ParseFloat: parsing "test": invalid syntax`,
   233  		},
   234  		{
   235  			msg:    "invalid hex data",
   236  			reader: strings.NewReader("Hex: test\n"),
   237  			value:  &[]record{},
   238  			err:    "encoding/hex: invalid byte: U+0074 't'",
   239  		},
   240  		{
   241  			msg:    "invalid hex length",
   242  			reader: strings.NewReader("Hex: FFFFFFFFFF\n"),
   243  			value:  &[]record{},
   244  			err:    "hex data would overflow byte array",
   245  		},
   246  		{
   247  			msg:    "invalid line",
   248  			reader: strings.NewReader("Foo\nBar:"),
   249  			value:  &[]record{},
   250  			err:    `invalid line: "Foo"`,
   251  		},
   252  		{
   253  			msg:    "missing colon",
   254  			reader: strings.NewReader("String"),
   255  			value:  &[]record{},
   256  			err:    "unexpected end of input",
   257  		},
   258  		{
   259  			msg:    "missing line feed",
   260  			reader: strings.NewReader("String:"),
   261  			value:  &[]record{},
   262  			err:    "unexpected end of input",
   263  		},
   264  		{
   265  			msg:    "unmarshaler error",
   266  			reader: strings.NewReader("Marshaler: test\n"),
   267  			value: &struct{ Marshaler errMarshaler }{
   268  				Marshaler: errMarshaler{errors.New("unmarshal error")},
   269  			},
   270  			err: "unmarshal error",
   271  		},
   272  	}
   273  
   274  	opts := test.CompareErrorMessages()
   275  
   276  	for _, test := range tests {
   277  		err := deb.NewDecoder(test.reader).Decode(test.value)
   278  		if diff := cmp.Diff(errors.New(test.err), err, opts); diff != "" {
   279  			t.Errorf("%s returned unexpected error:\n%s", test.msg, diff)
   280  		}
   281  	}
   282  }
   283  
   284  func BenchmarkUnmarshal(b *testing.B) {
   285  	type record struct {
   286  		String string
   287  		Hex    [4]byte
   288  		Int    int
   289  	}
   290  
   291  	in := []byte(`String: foo
   292   bar
   293   baz
   294  Hex: 01020304
   295  Int: 1
   296  
   297  String: foo
   298   bar
   299   baz
   300  Hex: 01020304
   301  Int: 1
   302  `)
   303  
   304  	var v []record
   305  
   306  	for i := 0; i < b.N; i++ {
   307  		deb.Unmarshal(in, &v)
   308  	}
   309  }