aboutsummaryrefslogtreecommitdiffstats
path: root/internal/xls/xls_test.go
blob: 71ed21a9d252be80563ba81ff438cf8cdbc43029 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package xls

import (
	"encoding/binary"
	"math"
	"reflect"
	"testing"
)

func rkInt(n int32, div100 bool) uint32 {
	rk := uint32(n)<<2 | 0x02
	if div100 {
		rk |= 0x01
	}
	return rk
}

func TestDecodeRK(t *testing.T) {
	tests := []struct {
		rk   uint32
		want float64
	}{
		{rkInt(1460, false), 1460},
		{rkInt(-677083, true), -6770.83},
		{rkInt(-1, false), -1},
		{rkInt(4670, true), 46.7},
		// Float form: the top 30 bits of an IEEE double.
		{uint32(math.Float64bits(-11800) >> 32), -11800},
	}
	for _, tt := range tests {
		if got := decodeRK(tt.rk); math.Abs(got-tt.want) > 1e-9 {
			t.Errorf("decodeRK(%#x) = %v, want %v", tt.rk, got, tt.want)
		}
	}
}

type builder struct{ b []byte }

func (w *builder) rec(typ uint16, data []byte) {
	w.b = binary.LittleEndian.AppendUint16(w.b, typ)
	w.b = binary.LittleEndian.AppendUint16(w.b, uint16(len(data)))
	w.b = append(w.b, data...)
}

func cellHead(row, col uint16) []byte {
	b := binary.LittleEndian.AppendUint16(nil, row)
	b = binary.LittleEndian.AppendUint16(b, col)
	return binary.LittleEndian.AppendUint16(b, 0) // xf
}

func TestParseWorkbook(t *testing.T) {
	// SST with two strings; the second ("Bærum", stored as UTF-16) is split
	// across a CONTINUE record, which restarts with its own option byte.
	sst := binary.LittleEndian.AppendUint32(nil, 2)
	sst = binary.LittleEndian.AppendUint32(sst, 2)
	sst = append(sst, 5, 0, 0)
	sst = append(sst, "Konto"...)
	sst = append(sst, 5, 0, 1, 'B', 0, 0xE6, 0)
	cont := []byte{0, 'r', 'u', 'm'}

	var sheet builder
	sheet.rec(recBOF, make([]byte, 16))
	sheet.rec(recLabelSST, binary.LittleEndian.AppendUint32(cellHead(0, 0), 0))
	sheet.rec(recLabelSST, binary.LittleEndian.AppendUint32(cellHead(0, 1), 1))
	sheet.rec(recRK, binary.LittleEndian.AppendUint32(cellHead(1, 0), rkInt(1460, false)))
	mulrk := binary.LittleEndian.AppendUint16(nil, 1)
	mulrk = binary.LittleEndian.AppendUint16(mulrk, 1)
	for _, v := range []uint32{rkInt(-677083, true), rkInt(5, false)} {
		mulrk = binary.LittleEndian.AppendUint16(mulrk, 0)
		mulrk = binary.LittleEndian.AppendUint32(mulrk, v)
	}
	mulrk = binary.LittleEndian.AppendUint16(mulrk, 2)
	sheet.rec(recMulRK, mulrk)
	sheet.rec(recNumber, binary.LittleEndian.AppendUint64(cellHead(2, 0), math.Float64bits(44587.125)))
	sheet.rec(recEOF, nil)

	// The globals substream, with the BOUNDSHEET offset patched once its
	// own length is known.
	var globals builder
	globals.rec(recBOF, make([]byte, 16))
	globals.rec(recBoundSheet, append(make([]byte, 6), 1, 0, 'S'))
	globals.rec(recSST, sst)
	globals.rec(recContinue, cont)
	globals.rec(recEOF, nil)
	binary.LittleEndian.PutUint32(globals.b[20+4:], uint32(len(globals.b)))

	grid, err := ParseWorkbook(append(globals.b, sheet.b...))
	if err != nil {
		t.Fatal(err)
	}
	want := [][]string{
		{"Konto", "Bærum"},
		{"1460", "-6770.83", "5"},
		{"44587.125"},
	}
	if !reflect.DeepEqual(grid, want) {
		t.Fatalf("grid = %q, want %q", grid, want)
	}
}