| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package utils |
| |
| import ( |
| "math" |
| "strings" |
| "testing" |
| |
| "github.com/stretchr/testify/assert" |
| ) |
| |
| func init() { |
| DebugHex = true |
| } |
| |
| func TestDump(t *testing.T) { |
| type args struct { |
| data []byte |
| } |
| tests := []struct { |
| name string |
| args args |
| want string |
| }{ |
| { |
| name: "Test Dump", |
| args: args{ |
| data: []byte("1234567890abcdefghijklmnopqrstuvwxyz"), |
| }, |
| want: ` |
| 00|31 32 33 34 35 36 37 38 39 30 '1234567890' |
| 10|61 62 63 64 65 66 67 68 69 6a 'abcdefghij' |
| 20|6b 6c 6d 6e 6f 70 71 72 73 74 'klmnopqrst' |
| 30|75 76 77 78 79 7a 'uvwxyz ' |
| `, |
| }, |
| { |
| name: "Test Bigger Dump", |
| args: args{ |
| data: []byte(strings.Repeat("Lorem ipsum", 90)), |
| }, |
| want: ` |
| 000|4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' |
| 010|6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' |
| 020|75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' |
| 030|73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' |
| 040|70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' |
| 050|69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' |
| 060|20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' |
| 070|6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' |
| 080|65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' |
| 090|72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' |
| 100|6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' |
| 110|4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' |
| 120|6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' |
| 130|75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' |
| 140|73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' |
| 150|70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' |
| 160|69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' |
| 170|20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' |
| 180|6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' |
| 190|65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' |
| 200|72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' |
| 210|6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' |
| 220|4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' |
| 230|6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' |
| 240|75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' |
| 250|73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' |
| 260|70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' |
| 270|69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' |
| 280|20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' |
| 290|6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' |
| 300|65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' |
| 310|72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' |
| 320|6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' |
| 330|4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' |
| 340|6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' |
| 350|75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' |
| 360|73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' |
| 370|70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' |
| 380|69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' |
| 390|20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' |
| 400|6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' |
| 410|65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' |
| 420|72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' |
| 430|6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' |
| 440|4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' |
| 450|6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' |
| 460|75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' |
| 470|73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' |
| 480|70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' |
| 490|69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' |
| 500|20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' |
| 510|6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' |
| 520|65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' |
| 530|72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' |
| 540|6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' |
| 550|4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' |
| 560|6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' |
| 570|75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' |
| 580|73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' |
| 590|70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' |
| 600|69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' |
| 610|20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' |
| 620|6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' |
| 630|65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' |
| 640|72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' |
| 650|6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' |
| 660|4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' |
| 670|6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' |
| 680|75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' |
| 690|73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' |
| 700|70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' |
| 710|69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' |
| 720|20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' |
| 730|6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' |
| 740|65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' |
| 750|72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' |
| 760|6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' |
| 770|4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' |
| 780|6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' |
| 790|75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' |
| 800|73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' |
| 810|70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' |
| 820|69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' |
| 830|20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' |
| 840|6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' |
| 850|65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' |
| 860|72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' |
| 870|6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' |
| 880|4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' |
| 890|6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' |
| 900|75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' |
| 910|73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' |
| 920|70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' |
| 930|69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' |
| 940|20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' |
| 950|6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' |
| 960|65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' |
| 970|72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' |
| 980|6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' |
| `, |
| }, |
| { |
| name: "no size", |
| args: args{ |
| []byte("a"), |
| }, |
| want: "0|61 'a '", |
| }, |
| } |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| if got := Dump(tt.args.data); got != strings.Trim(tt.want, "\n") { |
| t.Errorf("Dump() = \n%v\n, want \n%v\n", got, tt.want) |
| } |
| }) |
| } |
| } |
| |
| func BenchmarkTestDump(b *testing.B) { |
| DebugHex = false |
| type args struct { |
| data []byte |
| } |
| benchmarks := []struct { |
| name string |
| args args |
| }{ |
| { |
| "small", |
| args{ |
| data: []byte(strings.Repeat("Lorem ipsum", 1)), |
| }, |
| }, |
| { |
| "medium", |
| args{ |
| data: []byte(strings.Repeat("Lorem ipsum", 100)), |
| }, |
| }, |
| { |
| "big", |
| args{ |
| data: []byte(strings.Repeat("Lorem ipsum", 10000)), |
| }, |
| }, |
| } |
| for _, bm := range benchmarks { |
| b.Run(bm.name, func(b *testing.B) { |
| for i := 0; i < b.N; i++ { |
| Dump(bm.args.data) |
| } |
| }) |
| } |
| DebugHex = true |
| } |
| |
| func TestDumpFixedWidth(t *testing.T) { |
| type args struct { |
| data []byte |
| charWidth int |
| } |
| tests := []struct { |
| name string |
| args args |
| want string |
| }{ |
| { |
| name: "Test Dump", |
| args: args{ |
| data: []byte("1234567890abcdefghijklmnopqrstuvwxyz\3231234567890abcdefghijklmnopqrstuvwxyz\323aa1234567890abcdefghijklmnopqrstuvwxyz\3231234567890abcdefghijklmnopqrstuvwxyz\323aab"), |
| charWidth: 110, |
| }, |
| want: ` |
| 000|31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 '1234567890abcdefghijklmnop' |
| 026|71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 'qrstuvwxyz.1234567890abcde' |
| 052|66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 61 61 31 32 'fghijklmnopqrstuvwxyz.aa12' |
| 078|33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 '34567890abcdefghijklmnopqr' |
| 104|73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 'stuvwxyz.1234567890abcdefg' |
| 130|68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 61 61 62 'hijklmnopqrstuvwxyz.aab ' |
| `, |
| }, |
| { |
| name: "minimum size", |
| args: args{ |
| []byte("a"), |
| 1, |
| }, |
| want: "0|61 'a'", |
| }, |
| } |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| if got := DumpFixedWidth(tt.args.data, tt.args.charWidth); got != strings.Trim(tt.want, "\n") { |
| t.Errorf("Dump() = \n%v\n, want \n%v\n", got, tt.want) |
| } |
| }) |
| } |
| } |
| |
| func TestDiffHex(t *testing.T) { |
| type args struct { |
| expectedBytes, actualBytes []byte |
| } |
| tests := []struct { |
| name string |
| args args |
| want string |
| }{ |
| { |
| name: "two identical", |
| args: args{ |
| expectedBytes: []byte{1, 2, 3, 4}, |
| actualBytes: []byte{1, 2, 3, 4}, |
| }, |
| want: ` |
| ╔═expected═══════════════════════════════════╗╔═actual═════════════════════════════════════╗ |
| ║0|01 02 03 04 '.... '║║0|01 02 03 04 '.... '║ |
| ╚════════════════════════════════════════════╝╚════════════════════════════════════════════╝ |
| `, |
| }, |
| { |
| name: "two different", |
| args: args{ |
| expectedBytes: []byte{1, 2, 3, 4}, |
| actualBytes: []byte{1, 2, 6, 4}, |
| }, |
| want: ` |
| ╔═expected═══════════════════════════════════╗╔═actual═════════════════════════════════════╗ |
| ║0|01 02 [0;31m03 [0m04 '.... '║║0|01 02 [0;31m06 [0m04 '.... '║ |
| ╚════════════════════════════════════════════╝╚════════════════════════════════════════════╝ |
| `, |
| }, |
| } |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| if got := DiffHex(tt.args.expectedBytes, tt.args.actualBytes); got.String() != strings.Trim(tt.want, "\n") { |
| t.Errorf("Dump() = \n%v\n, want \n%v\n", got, tt.want) |
| } |
| }) |
| } |
| } |
| |
| func Test_maskString(t *testing.T) { |
| type args struct { |
| data []byte |
| } |
| tests := []struct { |
| name string |
| args args |
| want string |
| }{ |
| { |
| name: "printable chars", |
| args: args{[]byte("1234567890abcdefghijklmnopqrstuvwxyz")}, |
| want: "1234567890abcdefghijklmnopqrstuvwxyz", |
| }, |
| { |
| name: "unprintable chars", |
| args: args{[]byte("\3231234567890abcdefghijklmnopqrstuvwxyz.\323")}, |
| want: ".1234567890abcdefghijklmnopqrstuvwxyz..", |
| }, |
| } |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| if got := maskString(tt.args.data); got != tt.want { |
| t.Errorf("maskString() = %v, want %v", got, tt.want) |
| } |
| }) |
| } |
| } |
| |
| func Test_MinMax(t *testing.T) { |
| type args struct { |
| data []byte |
| charWidth int |
| } |
| tests := []struct { |
| name string |
| args args |
| want string |
| }{ |
| { |
| name: "nil data", |
| args: args{ |
| data: nil, |
| charWidth: math.MinInt32, |
| }, |
| want: "", |
| }, { |
| name: "empty data", |
| args: args{ |
| data: []byte{}, |
| charWidth: math.MinInt32, |
| }, |
| want: "", |
| }, |
| { |
| name: "-1 one byte", |
| args: args{ |
| data: []byte{0x1}, |
| charWidth: -1, |
| }, |
| want: "0|01 '.'", |
| }, |
| { |
| name: "12", |
| }, |
| } |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| if got := DumpFixedWidth(tt.args.data, tt.args.charWidth); got != tt.want { |
| t.Errorf("maskString() = %v, want %v", got, tt.want) |
| } |
| }) |
| } |
| } |
| |
| func Benchmark(b *testing.B) { |
| DebugHex = false |
| type args struct { |
| numberOfBytes, desiredStringWidth int |
| } |
| benchmarks := []struct { |
| name string |
| args args |
| }{ |
| { |
| "small", |
| args{ |
| numberOfBytes: 1, |
| desiredStringWidth: 120, |
| }, |
| }, |
| { |
| "medium", |
| args{ |
| numberOfBytes: 1000, |
| desiredStringWidth: 120, |
| }, |
| }, |
| { |
| "big", |
| args{ |
| numberOfBytes: 1000000, |
| desiredStringWidth: 120, |
| }, |
| }, |
| } |
| for _, bm := range benchmarks { |
| b.Run(bm.name, func(b *testing.B) { |
| for i := 0; i < b.N; i++ { |
| calculateBytesPerRowAndIndexWidth(bm.args.numberOfBytes, bm.args.desiredStringWidth) |
| } |
| }) |
| } |
| DebugHex = true |
| } |
| |
| func Test_calculateBytesPerRowAndIndexWidth(t *testing.T) { |
| type args struct { |
| numberOfBytes int |
| desiredCharWidth int |
| } |
| tests := []struct { |
| name string |
| args args |
| wantMaxBytesPerRow int |
| wantIndexWidth int |
| }{ |
| { |
| name: "1 byte MinInt32 width", |
| args: args{ |
| numberOfBytes: 1, |
| desiredCharWidth: math.MinInt32, |
| }, |
| wantMaxBytesPerRow: 1, |
| wantIndexWidth: 1, |
| }, |
| { |
| name: "10 byte MinInt32 width", |
| args: args{ |
| numberOfBytes: 10, |
| desiredCharWidth: math.MinInt32, |
| }, |
| wantMaxBytesPerRow: 1, |
| wantIndexWidth: 2, |
| }, |
| { |
| name: "100 byte MinInt32 width", |
| args: args{ |
| numberOfBytes: 100, |
| desiredCharWidth: math.MinInt32, |
| }, |
| wantMaxBytesPerRow: 1, |
| wantIndexWidth: 3, |
| }, |
| { |
| name: "100 byte MaxInt32 width", |
| args: args{ |
| numberOfBytes: 100, |
| desiredCharWidth: math.MaxInt32, |
| }, |
| wantMaxBytesPerRow: 536870910, |
| wantIndexWidth: 3, |
| }, |
| { |
| name: "100 byte 12 width", |
| args: args{ |
| numberOfBytes: 100, |
| desiredCharWidth: 12, |
| }, |
| wantMaxBytesPerRow: 1, |
| wantIndexWidth: 3, |
| }, |
| { |
| name: "153 byte 136 width", |
| args: args{ |
| numberOfBytes: 153, |
| desiredCharWidth: 136, |
| }, |
| wantMaxBytesPerRow: 32, |
| wantIndexWidth: 3, |
| }, |
| { |
| name: "153 byte calculated width", |
| args: args{ |
| numberOfBytes: 153, |
| desiredCharWidth: func() int { |
| const quoteRune = 1 |
| const numberOfBytes = 153 |
| const indexWidth = 3 |
| const charRepresentation = 1 |
| |
| // 000 AF FE AF FE ..... '....*' |
| return indexWidth + blankWidth + (numberOfBytes * byteWidth) + quoteRune + (numberOfBytes * charRepresentation) + quoteRune |
| }(), |
| }, |
| wantMaxBytesPerRow: 153, |
| wantIndexWidth: 3, |
| }, |
| } |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| got, got1 := calculateBytesPerRowAndIndexWidth(tt.args.numberOfBytes, tt.args.desiredCharWidth) |
| if got != tt.wantMaxBytesPerRow { |
| t.Errorf("calculateBytesPerRowAndIndexWidth() got max bytes per row = %v, want %v", got, tt.wantMaxBytesPerRow) |
| } |
| if got1 != tt.wantIndexWidth { |
| t.Errorf("calculateBytesPerRowAndIndexWidth() got index width = %v, want %v", got1, tt.wantIndexWidth) |
| } |
| }) |
| } |
| } |
| |
| func Test_Immutability(t *testing.T) { |
| inputBytes := []byte{0, 1, 2, 46, 56, 0, 200} |
| _ = Dump(inputBytes) |
| if !assert.Equal(t, []byte{0, 1, 2, 46, 56, 0, 200}, inputBytes) { |
| t.Errorf("Dump has mutated the array got:=%x, want:=%x", inputBytes, []byte{0, 1, 2, 46, 56, 0, 200}) |
| } |
| } |