github.com/wfusion/gofusion@v1.1.14/test/common/utils/cases/encode_test.go (about) 1 package cases 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "math/rand" 8 "strings" 9 "sync" 10 "testing" 11 "time" 12 13 "github.com/stretchr/testify/suite" 14 15 "github.com/wfusion/gofusion/common/utils" 16 "github.com/wfusion/gofusion/common/utils/cipher" 17 "github.com/wfusion/gofusion/common/utils/compress" 18 "github.com/wfusion/gofusion/common/utils/encode" 19 "github.com/wfusion/gofusion/log" 20 21 testUtl "github.com/wfusion/gofusion/test/common/utils" 22 ) 23 24 func TestEncode(t *testing.T) { 25 t.Parallel() 26 testingSuite := &Encode{Test: new(testUtl.Test)} 27 suite.Run(t, testingSuite) 28 } 29 30 type Encode struct { 31 *testUtl.Test 32 } 33 34 func (t *Encode) BeforeTest(suiteName, testName string) { 35 t.Catch(func() { 36 log.Info(context.Background(), "right before %s %s", suiteName, testName) 37 }) 38 } 39 40 func (t *Encode) AfterTest(suiteName, testName string) { 41 t.Catch(func() { 42 log.Info(context.Background(), "right after %s %s", suiteName, testName) 43 }) 44 } 45 46 func (t *Encode) TestCodecSingleCombination() { 47 cases := [][]encode.EncodedType{ 48 {encode.EncodedTypeCipher}, 49 {encode.EncodedTypeCompress}, 50 {encode.EncodedTypeEncode}, 51 } 52 t.runCodecCases(cases) 53 } 54 55 func (t *Encode) TestCodecDoubleCombinations() { 56 cases := [][]encode.EncodedType{ 57 {encode.EncodedTypeCipher, encode.EncodedTypeCompress}, 58 {encode.EncodedTypeCompress, encode.EncodedTypeCipher}, 59 {encode.EncodedTypeCipher, encode.EncodedTypeEncode}, 60 {encode.EncodedTypeEncode, encode.EncodedTypeCipher}, 61 {encode.EncodedTypeCompress, encode.EncodedTypeEncode}, 62 {encode.EncodedTypeEncode, encode.EncodedTypeCompress}, 63 } 64 65 t.runCodecCases(cases) 66 } 67 68 func (t *Encode) TestCodecTripleCombinations() { 69 cases := [][]encode.EncodedType{ 70 {encode.EncodedTypeCipher, encode.EncodedTypeCompress, encode.EncodedTypeEncode}, 71 {encode.EncodedTypeCipher, encode.EncodedTypeEncode, encode.EncodedTypeCompress}, 72 {encode.EncodedTypeCompress, encode.EncodedTypeCipher, encode.EncodedTypeEncode}, 73 {encode.EncodedTypeCompress, encode.EncodedTypeEncode, encode.EncodedTypeCipher}, 74 {encode.EncodedTypeEncode, encode.EncodedTypeCompress, encode.EncodedTypeCipher}, 75 {encode.EncodedTypeEncode, encode.EncodedTypeCipher, encode.EncodedTypeCompress}, 76 } 77 78 t.runCodecCases(cases) 79 } 80 81 func (t *Encode) TestStreamSingleCombination() { 82 cases := [][]encode.EncodedType{ 83 {encode.EncodedTypeCipher}, 84 {encode.EncodedTypeCompress}, 85 {encode.EncodedTypeEncode}, 86 } 87 88 t.runStreamCases(cases) 89 t.runStreamParallelCases(cases) 90 } 91 92 func (t *Encode) TestStreamDoubleCombinations() { 93 cases := [][]encode.EncodedType{ 94 {encode.EncodedTypeCipher, encode.EncodedTypeCompress}, 95 {encode.EncodedTypeCompress, encode.EncodedTypeCipher}, 96 {encode.EncodedTypeCipher, encode.EncodedTypeEncode}, 97 {encode.EncodedTypeEncode, encode.EncodedTypeCipher}, 98 {encode.EncodedTypeCompress, encode.EncodedTypeEncode}, 99 {encode.EncodedTypeEncode, encode.EncodedTypeCompress}, 100 } 101 102 t.runStreamCases(cases) 103 t.runStreamParallelCases(cases) 104 } 105 106 func (t *Encode) TestStreamTripleCombinations() { 107 cases := [][]encode.EncodedType{ 108 {encode.EncodedTypeCipher, encode.EncodedTypeCompress, encode.EncodedTypeEncode}, 109 {encode.EncodedTypeCipher, encode.EncodedTypeEncode, encode.EncodedTypeCompress}, 110 {encode.EncodedTypeCompress, encode.EncodedTypeCipher, encode.EncodedTypeEncode}, 111 {encode.EncodedTypeCompress, encode.EncodedTypeEncode, encode.EncodedTypeCipher}, 112 {encode.EncodedTypeEncode, encode.EncodedTypeCompress, encode.EncodedTypeCipher}, 113 {encode.EncodedTypeEncode, encode.EncodedTypeCipher, encode.EncodedTypeCompress}, 114 } 115 116 t.runStreamCases(cases) 117 t.runStreamParallelCases(cases) 118 } 119 120 func (t *Encode) runCodecCases(cases [][]encode.EncodedType) { 121 t.Catch(func() { 122 data := t.smallRandomData() 123 for _, cs := range cases { 124 for _, testCase := range t.generateCodecCases(cs...) { 125 t.Run(strings.Join(testCase.names, "->"), func() { 126 options := testCase.extenders 127 reversed := make([]utils.OptionExtender, len(options)) 128 for i, v := range options { 129 reversed[len(options)-1-i] = v 130 } 131 132 // case: encode then decode 133 encoded, err := encode.From(data).Encode(options...).ToBytes() 134 t.NoError(err) 135 t.NotEqualValues(data, encoded) 136 137 codec := encode.From(encoded) 138 for i := 0; i < len(reversed); i++ { 139 codec = codec.Decode(reversed[i]) 140 } 141 decoded, err := codec.ToString() 142 t.NoError(err) 143 t.EqualValues(data, []byte(decoded)) 144 145 // case: encoded and decode together 146 actual, err := encode. 147 From(data). 148 Encode(options...). 149 Decode(reversed...). 150 ToBytes() 151 t.NoError(err) 152 t.EqualValues(data, actual) 153 }) 154 } 155 } 156 }) 157 } 158 159 func (t *Encode) runStreamCases(cases [][]encode.EncodedType) { 160 t.Catch(func() { 161 data := t.randomData() 162 for _, cs := range cases { 163 for _, testCase := range t.generateStreamCases(cs...) { 164 t.Run(strings.Join(testCase.names, "->"), func() { 165 codecStream := encode.NewCodecStream(testCase.extenders...) 166 dataBuffer := bytes.NewReader(data) 167 168 encodedBuffer := bytes.NewBuffer(nil) 169 _, err := codecStream.Encode(encodedBuffer, dataBuffer) 170 t.NoError(err) 171 t.NotZero(encodedBuffer.Len()) 172 t.NotEqualValues(data, encodedBuffer.Bytes()) 173 174 decodedBuffer := bytes.NewBuffer(nil) 175 _, err = codecStream.Decode(decodedBuffer, encodedBuffer) 176 t.NoError(err) 177 178 t.EqualValues(data, decodedBuffer.Bytes()) 179 }) 180 } 181 } 182 }) 183 } 184 185 func (t *Encode) runStreamParallelCases(cases [][]encode.EncodedType) { 186 t.Catch(func() { 187 t.Run(fmt.Sprintf("stream-combination-%v-parallel", len(cases[0])), func() { 188 data := t.randomData() 189 wg := new(sync.WaitGroup) 190 defer wg.Wait() 191 for _, cs := range cases { 192 for _, item := range t.generateStreamCases(cs...) { 193 wg.Add(1) 194 195 testCase := item 196 go func() { 197 defer wg.Done() 198 199 codecStream := encode.NewCodecStream(testCase.extenders...) 200 dataBuffer := bytes.NewReader(data) 201 202 encodedBuffer := bytes.NewBuffer(nil) 203 _, err := codecStream.Encode(encodedBuffer, dataBuffer) 204 t.NoError(err) 205 t.NotZero(encodedBuffer.Len()) 206 t.NotEqualValues(data, encodedBuffer.Bytes()) 207 208 decodedBuffer := bytes.NewBuffer(nil) 209 _, err = codecStream.Decode(decodedBuffer, encodedBuffer) 210 t.NoError(err) 211 212 t.EqualValues(data, decodedBuffer.Bytes()) 213 }() 214 } 215 } 216 }) 217 }) 218 } 219 220 func (t *Encode) randomData() (data []byte) { 221 const ( 222 jitterLength = 4 * 1024 // 4kb 223 largeLength = 1024*1024 - jitterLength/2 // 1m - 2kb 224 ) 225 226 // 1m ± 2kb 227 data = make([]byte, largeLength+rand.Int()%(jitterLength/2)) 228 //data = make([]byte, 10) 229 _, err := utils.Random(data, utils.GetTimeStamp(time.Now())) 230 t.NoError(err) 231 return 232 } 233 234 func (t *Encode) smallRandomData() (data []byte) { 235 const ( 236 jitterLength = 4 * 1024 // 4kb 237 smallLength = 32*1024 - jitterLength/2 // 32kb - 2kb 238 ) 239 240 // 32kb ± 2kb 241 data = make([]byte, smallLength+rand.Int()%(jitterLength/2)) 242 _, err := utils.Random(data, utils.GetTimeStamp(time.Now())) 243 t.NoError(err) 244 return 245 } 246 247 func (t *Encode) generateCodecCases(types ...encode.EncodedType) (options []*option) { 248 opts := make(optionList, 0, len(types)) 249 for _, typ := range types { 250 switch typ { 251 case encode.EncodedTypeCipher: 252 opts = append(opts, t.cipherOptions()) 253 case encode.EncodedTypeCompress: 254 opts = append(opts, t.compressOptions()) 255 case encode.EncodedTypeEncode: 256 opts = append(opts, t.printableOptions()) 257 } 258 } 259 260 return opts.combine() 261 } 262 263 func (t *Encode) generateStreamCases(types ...encode.EncodedType) (options []*option) { 264 opts := make(optionList, 0, len(types)) 265 for _, typ := range types { 266 switch typ { 267 case encode.EncodedTypeCipher: 268 opts = append(opts, t.cipherStreamOptions()) 269 case encode.EncodedTypeCompress: 270 opts = append(opts, t.compressOptions()) 271 case encode.EncodedTypeEncode: 272 opts = append(opts, t.printableOptions()) 273 } 274 } 275 276 return opts.combine() 277 } 278 279 func (t *Encode) cipherStreamOptions() (opt *option) { 280 opt = new(option) 281 282 cipherModes := []cipher.Mode{ 283 cipher.ModeCFB, 284 cipher.ModeCTR, 285 cipher.ModeOFB, 286 cipher.ModeGCM, 287 } 288 289 // cipher Options 290 var ( 291 k1 [1]byte 292 k8, iv8 [8]byte 293 k16, iv16 [16]byte 294 k24 [24]byte 295 k32 [32]byte 296 k256 [256]byte 297 ) 298 _, err := utils.Random(k1[:], utils.GetTimeStamp(time.Now())) 299 t.NoError(err) 300 _, err = utils.Random(k8[:], utils.GetTimeStamp(time.Now())) 301 t.NoError(err) 302 _, err = utils.Random(iv8[:], utils.GetTimeStamp(time.Now())) 303 t.NoError(err) 304 _, err = utils.Random(k16[:], utils.GetTimeStamp(time.Now())) 305 t.NoError(err) 306 _, err = utils.Random(iv16[:], utils.GetTimeStamp(time.Now())) 307 t.NoError(err) 308 _, err = utils.Random(k24[:], utils.GetTimeStamp(time.Now())) 309 t.NoError(err) 310 _, err = utils.Random(k32[:], utils.GetTimeStamp(time.Now())) 311 t.NoError(err) 312 _, err = utils.Random(k256[:], utils.GetTimeStamp(time.Now())) 313 t.NoError(err) 314 315 for _, mode := range cipherModes { 316 if mode != cipher.ModeGCM { 317 opt.names = append(opt.names, cipher.AlgorithmDES.String()+"-"+mode.String()) 318 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmDES, mode, k8[:], iv8[:])) 319 320 opt.names = append(opt.names, cipher.Algorithm3DES.String()+"-"+mode.String()) 321 opt.extenders = append(opt.extenders, encode.Cipher(cipher.Algorithm3DES, mode, k24[:], iv8[:])) 322 } 323 324 opt.names = append(opt.names, cipher.AlgorithmAES.String()+"-128-"+mode.String()) 325 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmAES, mode, k16[:], iv16[:])) 326 327 opt.names = append(opt.names, cipher.AlgorithmAES.String()+"-192-"+mode.String()) 328 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmAES, mode, k24[:], iv16[:])) 329 330 opt.names = append(opt.names, cipher.AlgorithmAES.String()+"-256-"+mode.String()) 331 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmAES, mode, k32[:], iv16[:])) 332 } 333 334 opt.names = append(opt.names, cipher.AlgorithmRC4.String()+"-8") 335 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmRC4, 0, k1[:], nil)) 336 337 opt.names = append(opt.names, cipher.AlgorithmRC4.String()+"-256") 338 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmRC4, 0, k32[:], nil)) 339 340 opt.names = append(opt.names, cipher.AlgorithmRC4.String()+"-1024") 341 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmRC4, 0, k256[:], nil)) 342 343 opt.names = append(opt.names, cipher.AlgorithmChaCha20poly1305.String()) 344 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmChaCha20poly1305, 0, k32[:], nil)) 345 346 opt.names = append(opt.names, cipher.AlgorithmXChaCha20poly1305.String()) 347 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmXChaCha20poly1305, 0, k32[:], nil)) 348 349 return 350 } 351 352 func (t *Encode) cipherOptions() (opt *option) { 353 opt = new(option) 354 355 cipherModes := []cipher.Mode{ 356 cipher.ModeECB, 357 cipher.ModeCBC, 358 cipher.ModeCFB, 359 cipher.ModeCTR, 360 cipher.ModeOFB, 361 cipher.ModeGCM, 362 } 363 364 // cipher Options 365 var ( 366 k1 [1]byte 367 k8, iv8 [8]byte 368 k16, iv16 [16]byte 369 k24 [24]byte 370 k32 [32]byte 371 k256 [256]byte 372 ) 373 _, err := utils.Random(k1[:], utils.GetTimeStamp(time.Now())) 374 t.NoError(err) 375 _, err = utils.Random(k8[:], utils.GetTimeStamp(time.Now())) 376 t.NoError(err) 377 _, err = utils.Random(iv8[:], utils.GetTimeStamp(time.Now())) 378 t.NoError(err) 379 _, err = utils.Random(k16[:], utils.GetTimeStamp(time.Now())) 380 t.NoError(err) 381 _, err = utils.Random(iv16[:], utils.GetTimeStamp(time.Now())) 382 t.NoError(err) 383 _, err = utils.Random(k24[:], utils.GetTimeStamp(time.Now())) 384 t.NoError(err) 385 _, err = utils.Random(k32[:], utils.GetTimeStamp(time.Now())) 386 t.NoError(err) 387 _, err = utils.Random(k256[:], utils.GetTimeStamp(time.Now())) 388 t.NoError(err) 389 390 for _, mode := range cipherModes { 391 if mode != cipher.ModeGCM { 392 opt.names = append(opt.names, cipher.AlgorithmDES.String()+"-"+mode.String()) 393 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmDES, mode, k8[:], iv8[:])) 394 395 opt.names = append(opt.names, cipher.Algorithm3DES.String()+"-"+mode.String()) 396 opt.extenders = append(opt.extenders, encode.Cipher(cipher.Algorithm3DES, mode, k24[:], iv8[:])) 397 } 398 399 opt.names = append(opt.names, cipher.AlgorithmAES.String()+"-128-"+mode.String()) 400 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmAES, mode, k16[:], iv16[:])) 401 402 opt.names = append(opt.names, cipher.AlgorithmAES.String()+"-192-"+mode.String()) 403 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmAES, mode, k24[:], iv16[:])) 404 405 opt.names = append(opt.names, cipher.AlgorithmAES.String()+"-256-"+mode.String()) 406 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmAES, mode, k32[:], iv16[:])) 407 408 opt.names = append(opt.names, cipher.AlgorithmSM4.String()+"-"+mode.String()) 409 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmSM4, mode, k16[:], iv16[:])) 410 } 411 412 opt.names = append(opt.names, cipher.AlgorithmRC4.String()+"-8") 413 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmRC4, 0, k1[:], nil)) 414 415 opt.names = append(opt.names, cipher.AlgorithmRC4.String()+"-256") 416 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmRC4, 0, k32[:], nil)) 417 418 opt.names = append(opt.names, cipher.AlgorithmRC4.String()+"-1024") 419 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmRC4, 0, k256[:], nil)) 420 421 opt.names = append(opt.names, cipher.AlgorithmChaCha20poly1305.String()) 422 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmChaCha20poly1305, 0, k32[:], nil)) 423 424 opt.names = append(opt.names, cipher.AlgorithmXChaCha20poly1305.String()) 425 opt.extenders = append(opt.extenders, encode.Cipher(cipher.AlgorithmXChaCha20poly1305, 0, k32[:], nil)) 426 427 return 428 } 429 430 func (t *Encode) compressOptions() (opt *option) { 431 opt = new(option) 432 433 algos := []compress.Algorithm{ 434 compress.AlgorithmZSTD, 435 compress.AlgorithmZLib, 436 compress.AlgorithmS2, 437 compress.AlgorithmGZip, 438 compress.AlgorithmDeflate, 439 } 440 441 opt.names = make([]string, 0, len(algos)) 442 opt.extenders = make([]utils.OptionExtender, 0, len(algos)) 443 for _, algo := range algos { 444 opt.names = append(opt.names, algo.String()) 445 opt.extenders = append(opt.extenders, encode.Compress(algo)) 446 } 447 return 448 } 449 450 func (t *Encode) printableOptions() (opt *option) { 451 opt = new(option) 452 453 algos := []encode.Algorithm{ 454 encode.AlgorithmHex, 455 encode.AlgorithmBase32Std, 456 encode.AlgorithmBase32Hex, 457 encode.AlgorithmBase64Std, 458 encode.AlgorithmBase64URL, 459 encode.AlgorithmBase64RawStd, 460 encode.AlgorithmBase64RawURL, 461 } 462 463 opt.names = make([]string, 0, len(algos)) 464 opt.extenders = make([]utils.OptionExtender, 0, len(algos)) 465 for _, algo := range algos { 466 opt.names = append(opt.names, algo.String()) 467 opt.extenders = append(opt.extenders, encode.Encode(algo)) 468 } 469 return 470 } 471 472 type option struct { 473 names []string 474 extenders []utils.OptionExtender 475 } 476 477 type optionList []*option 478 479 func (o optionList) combine() (result []*option) { 480 type element struct { 481 depth int 482 current *option 483 } 484 queue := []*element{ 485 { 486 depth: 0, 487 current: new(option), 488 }, 489 } 490 for len(queue) > 0 { 491 elem := queue[0] 492 queue = queue[1:] 493 494 if elem.depth == len(o) { 495 if len(elem.current.extenders) == len(o) { 496 result = append(result, elem.current) 497 } 498 continue 499 } 500 501 opt := o[elem.depth] 502 if len(opt.extenders) == 0 { 503 queue = append(queue, &element{ 504 depth: elem.depth + 1, 505 current: &option{ 506 names: elem.current.names, 507 extenders: elem.current.extenders, 508 }, 509 }) 510 continue 511 } 512 513 for i := 0; i < len(opt.extenders); i++ { 514 newElem := &element{ 515 depth: elem.depth + 1, 516 current: &option{ 517 names: append(elem.current.names, opt.names[i]), 518 extenders: append(elem.current.extenders, opt.extenders[i]), 519 }, 520 } 521 queue = append(queue, newElem) 522 } 523 } 524 525 return 526 }