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 }