Skip to content

Commit dd33123

Browse files
committed
extract byte diff comparison test utility into common testhelper
Signed-off-by: Avi Deitcher <[email protected]>
1 parent 65b061b commit dd33123

File tree

5 files changed

+143
-139
lines changed

5 files changed

+143
-139
lines changed

filesystem/ext4/directory_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package ext4
22

33
import (
44
"testing"
5+
6+
"github.com/diskfs/go-diskfs/testhelper"
57
)
68

79
func TestDirectoryToBytes(t *testing.T) {
@@ -17,7 +19,7 @@ func TestDirectoryToBytes(t *testing.T) {
1719
b := dir.toBytes(bytesPerBlock, directoryChecksumAppender(sb.checksumSeed, 2, 0))
1820

1921
// read the bytes from the disk
20-
diff, diffString := dumpByteSlicesWithDiffs(b, expected, 32, false, true, true)
22+
diff, diffString := testhelper.DumpByteSlicesWithDiffs(b, expected, 32, false, true, true)
2123
if diff {
2224
t.Errorf("directory.toBytes() mismatched, actual then expected\n%s", diffString)
2325
}

filesystem/ext4/groupdescriptors_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66
"testing"
77

8+
"github.com/diskfs/go-diskfs/testhelper"
89
"github.com/go-test/deep"
910
)
1011

@@ -60,7 +61,7 @@ func TestGroupDescriptorToBytes(t *testing.T) {
6061
}
6162
b := gd.toBytes(sb.gdtChecksumType(), sb.checksumSeed)
6263
expected = expected[:64]
63-
diff, diffString := dumpByteSlicesWithDiffs(b, expected, 32, false, true, true)
64+
diff, diffString := testhelper.DumpByteSlicesWithDiffs(b, expected, 32, false, true, true)
6465
if diff {
6566
t.Errorf("groupdescriptor.toBytes() mismatched, actual then expected\n%s", diffString)
6667
}
@@ -94,7 +95,7 @@ func TestGroupDescriptorsToBytes(t *testing.T) {
9495
descriptors: groupdescriptors,
9596
}
9697
b := gds.toBytes(sb.gdtChecksumType(), sb.checksumSeed)
97-
diff, diffString := dumpByteSlicesWithDiffs(b, expected, 32, false, true, true)
98+
diff, diffString := testhelper.DumpByteSlicesWithDiffs(b, expected, 32, false, true, true)
9899
if diff {
99100
t.Errorf("groupDescriptors.toBytes() mismatched, actual then expected\n%s", diffString)
100101
}

filesystem/ext4/superblock_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"reflect"
55
"testing"
66

7+
"github.com/diskfs/go-diskfs/testhelper"
78
"github.com/go-test/deep"
89
)
910

@@ -32,7 +33,7 @@ func TestSuperblockToBytes(t *testing.T) {
3233
if err != nil {
3334
t.Fatalf("Failed to serialize superblock: %v", err)
3435
}
35-
diff, diffString := dumpByteSlicesWithDiffs(b, expected, 32, false, true, true)
36+
diff, diffString := testhelper.DumpByteSlicesWithDiffs(b, expected, 32, false, true, true)
3637
if diff {
3738
t.Errorf("superblock.toBytes() mismatched, actual then expected\n%s", diffString)
3839
}

filesystem/ext4/util_test.go

-135
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package ext4
22

33
import (
44
"bytes"
5-
"fmt"
65
"testing"
76
)
87

@@ -64,137 +63,3 @@ func TestMinString(t *testing.T) {
6463
})
6564
}
6665
}
67-
68-
// dumpByteSlice dump a byte slice in hex and optionally ASCII format.
69-
// Optionally but position at the beginning of each row, like xxd.
70-
// Optionally convert to ASCII at end of each row, like xxd.
71-
// Can show positions at beginning of each row in hex, decimal or both.
72-
// Can filter out all rows except those containing given positions in showOnlyBytes. If showOnlyBytes is nil, all rows are shown.
73-
// If showOnlyBytes is not nil, even an empty slice, will only show those rows that contain the given positions.
74-
func dumpByteSlice(b []byte, bytesPerRow int, showASCII, showPosHex, showPosDec bool, showOnlyBytes []int) (out string) {
75-
var ascii []byte
76-
// go through each byte.
77-
// At each position:
78-
// - if we are at the end of a row, print the ASCII representation of the row.
79-
// - if we are at the middle of a row, add an extra space
80-
// - if we are still in the byte slice, print the byte in hex with a space before it.
81-
// - if we are past the end of the row, print spaces.
82-
showOnlyMap := make(map[int]bool)
83-
for _, v := range showOnlyBytes {
84-
showOnlyMap[v] = true
85-
}
86-
// run by rows
87-
numRows := len(b) / bytesPerRow
88-
if len(b)%bytesPerRow != 0 {
89-
numRows++
90-
}
91-
for i := 0; i < numRows; i++ {
92-
firstByte := i * bytesPerRow
93-
lastByte := firstByte + bytesPerRow
94-
var row string
95-
// row header includes optional position numbers
96-
if showPosHex {
97-
row += fmt.Sprintf("%08x ", firstByte)
98-
}
99-
if showPosDec {
100-
row += fmt.Sprintf("%4d ", firstByte)
101-
}
102-
row += ": "
103-
for j := firstByte; j < lastByte; j++ {
104-
// every 8 bytes add extra spacing to make it easier to read
105-
if j%8 == 0 {
106-
row += " "
107-
}
108-
// regular byte, print in hex
109-
if j < len(b) {
110-
hex := fmt.Sprintf(" %02x", b[j])
111-
if showOnlyBytes != nil && showOnlyMap[j] {
112-
hex = "\033[1m\033[31m" + hex + "\033[0m"
113-
}
114-
row += hex
115-
} else {
116-
row += " "
117-
}
118-
switch {
119-
case j >= len(b):
120-
// past end of byte slice, print spaces
121-
ascii = append(ascii, ' ')
122-
case b[j] < 32 || b[j] > 126:
123-
// unprintable characters, print a dot
124-
ascii = append(ascii, '.')
125-
default:
126-
// printable characters, print the character
127-
ascii = append(ascii, b[j])
128-
}
129-
}
130-
// end of row, print the ASCII representation and a newline
131-
if showASCII {
132-
row += fmt.Sprintf(" %s", string(ascii))
133-
ascii = ascii[:0]
134-
}
135-
row += "\n"
136-
137-
// calculate if we should include this row
138-
var includeRow = true
139-
if showOnlyBytes != nil {
140-
includeRow = false
141-
for j := firstByte; j < lastByte; j++ {
142-
if showOnlyMap[j] {
143-
includeRow = true
144-
break
145-
}
146-
}
147-
}
148-
if includeRow {
149-
out += row
150-
}
151-
}
152-
return out
153-
}
154-
155-
// diff
156-
type diff struct {
157-
Offset int
158-
ByteA byte
159-
ByteB byte
160-
}
161-
162-
// compareByteSlices compares two byte slices position by position. If the byte slices are identical, diffs is length 0,
163-
// otherwise it contains the positions of the differences.
164-
func compareByteSlices(a, b []byte) (diffs []diff) {
165-
maxSize := len(a)
166-
if len(b) > maxSize {
167-
maxSize = len(b)
168-
}
169-
for i := 0; i < maxSize; i++ {
170-
switch {
171-
case i >= len(a):
172-
diffs = append(diffs, diff{Offset: i, ByteA: 0, ByteB: b[i]})
173-
case i >= len(b):
174-
diffs = append(diffs, diff{Offset: i, ByteA: a[i], ByteB: 0})
175-
case a[i] != b[i]:
176-
diffs = append(diffs, diff{Offset: i, ByteA: a[i], ByteB: b[i]})
177-
}
178-
}
179-
return diffs
180-
}
181-
182-
// dumpByteSlicesWithDiffs show two byte slices in hex and ASCII format, with differences highlighted.
183-
//
184-
//nolint:unparam // sure, bytesPerRow always is 32, but it could be something else
185-
func dumpByteSlicesWithDiffs(a, b []byte, bytesPerRow int, showASCII, showPosHex, showPosDec bool) (different bool, out string) {
186-
diffs := compareByteSlices(a, b)
187-
// if there are no differences, just return an empty string
188-
if len(diffs) == 0 {
189-
return false, ""
190-
}
191-
192-
showOnlyBytes := make([]int, len(diffs))
193-
for i, d := range diffs {
194-
showOnlyBytes[i] = d.Offset
195-
}
196-
out = dumpByteSlice(a, bytesPerRow, showASCII, showPosHex, showPosDec, showOnlyBytes)
197-
out += "\n"
198-
out += dumpByteSlice(b, bytesPerRow, showASCII, showPosHex, showPosDec, showOnlyBytes)
199-
return true, out
200-
}

testhelper/diff.go

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package testhelper
2+
3+
import "fmt"
4+
5+
// dumpByteSlice dump a byte slice in hex and optionally ASCII format.
6+
// Optionally but position at the beginning of each row, like xxd.
7+
// Optionally convert to ASCII at end of each row, like xxd.
8+
// Can show positions at beginning of each row in hex, decimal or both.
9+
// Can filter out all rows except those containing given positions in showOnlyBytes. If showOnlyBytes is nil, all rows are shown.
10+
// If showOnlyBytes is not nil, even an empty slice, will only show those rows that contain the given positions.
11+
func dumpByteSlice(b []byte, bytesPerRow int, showASCII, showPosHex, showPosDec bool, showOnlyBytes []int) (out string) {
12+
var ascii []byte
13+
// go through each byte.
14+
// At each position:
15+
// - if we are at the end of a row, print the ASCII representation of the row.
16+
// - if we are at the middle of a row, add an extra space
17+
// - if we are still in the byte slice, print the byte in hex with a space before it.
18+
// - if we are past the end of the row, print spaces.
19+
showOnlyMap := make(map[int]bool)
20+
for _, v := range showOnlyBytes {
21+
showOnlyMap[v] = true
22+
}
23+
// run by rows
24+
numRows := len(b) / bytesPerRow
25+
if len(b)%bytesPerRow != 0 {
26+
numRows++
27+
}
28+
for i := 0; i < numRows; i++ {
29+
firstByte := i * bytesPerRow
30+
lastByte := firstByte + bytesPerRow
31+
var row string
32+
// row header includes optional position numbers
33+
if showPosHex {
34+
row += fmt.Sprintf("%08x ", firstByte)
35+
}
36+
if showPosDec {
37+
row += fmt.Sprintf("%4d ", firstByte)
38+
}
39+
row += ": "
40+
for j := firstByte; j < lastByte; j++ {
41+
// every 8 bytes add extra spacing to make it easier to read
42+
if j%8 == 0 {
43+
row += " "
44+
}
45+
// regular byte, print in hex
46+
if j < len(b) {
47+
hex := fmt.Sprintf(" %02x", b[j])
48+
if showOnlyBytes != nil && showOnlyMap[j] {
49+
hex = "\033[1m\033[31m" + hex + "\033[0m"
50+
}
51+
row += hex
52+
} else {
53+
row += " "
54+
}
55+
switch {
56+
case j >= len(b):
57+
// past end of byte slice, print spaces
58+
ascii = append(ascii, ' ')
59+
case b[j] < 32 || b[j] > 126:
60+
// unprintable characters, print a dot
61+
ascii = append(ascii, '.')
62+
default:
63+
// printable characters, print the character
64+
ascii = append(ascii, b[j])
65+
}
66+
}
67+
// end of row, print the ASCII representation and a newline
68+
if showASCII {
69+
row += fmt.Sprintf(" %s", string(ascii))
70+
ascii = ascii[:0]
71+
}
72+
row += "\n"
73+
74+
// calculate if we should include this row
75+
var includeRow = true
76+
if showOnlyBytes != nil {
77+
includeRow = false
78+
for j := firstByte; j < lastByte; j++ {
79+
if showOnlyMap[j] {
80+
includeRow = true
81+
break
82+
}
83+
}
84+
}
85+
if includeRow {
86+
out += row
87+
}
88+
}
89+
return out
90+
}
91+
92+
// diff
93+
type diff struct {
94+
Offset int
95+
ByteA byte
96+
ByteB byte
97+
}
98+
99+
// compareByteSlices compares two byte slices position by position. If the byte slices are identical, diffs is length 0,
100+
// otherwise it contains the positions of the differences.
101+
func compareByteSlices(a, b []byte) (diffs []diff) {
102+
maxSize := len(a)
103+
if len(b) > maxSize {
104+
maxSize = len(b)
105+
}
106+
for i := 0; i < maxSize; i++ {
107+
switch {
108+
case i >= len(a):
109+
diffs = append(diffs, diff{Offset: i, ByteA: 0, ByteB: b[i]})
110+
case i >= len(b):
111+
diffs = append(diffs, diff{Offset: i, ByteA: a[i], ByteB: 0})
112+
case a[i] != b[i]:
113+
diffs = append(diffs, diff{Offset: i, ByteA: a[i], ByteB: b[i]})
114+
}
115+
}
116+
return diffs
117+
}
118+
119+
// dumpByteSlicesWithDiffs show two byte slices in hex and ASCII format, with differences highlighted.
120+
func DumpByteSlicesWithDiffs(a, b []byte, bytesPerRow int, showASCII, showPosHex, showPosDec bool) (different bool, out string) {
121+
diffs := compareByteSlices(a, b)
122+
// if there are no differences, just return an empty string
123+
if len(diffs) == 0 {
124+
return false, ""
125+
}
126+
127+
showOnlyBytes := make([]int, len(diffs))
128+
for i, d := range diffs {
129+
showOnlyBytes[i] = d.Offset
130+
}
131+
out = dumpByteSlice(a, bytesPerRow, showASCII, showPosHex, showPosDec, showOnlyBytes)
132+
out += "\n"
133+
out += dumpByteSlice(b, bytesPerRow, showASCII, showPosHex, showPosDec, showOnlyBytes)
134+
return true, out
135+
}

0 commit comments

Comments
 (0)