Skip to content

Commit 1adc61a

Browse files
committed
script: Fix comparison of non-ordered columns
The takeColumns function assumed that the column indexes were ordered, e.g. if the table header was "Foo Bar Baz", then it'd always be referenced in order: "Foo" or "Foo Baz" but never "Baz Foo". There's no reason to assume that, so fix this and add tests. Signed-off-by: Jussi Maki <[email protected]>
1 parent 6035290 commit 1adc61a

File tree

2 files changed

+69
-5
lines changed

2 files changed

+69
-5
lines changed

script.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -649,12 +649,11 @@ func writeObjects(tbl *AnyTable, it iter.Seq2[any, Revision], w io.Writer, colum
649649
return fmt.Errorf("unknown format %q, expected table, yaml or json", format)
650650
}
651651

652-
func takeColumns[T any](xs []T, idxs []int) []T {
653-
// Invariant: idxs is sorted so can set in-place.
654-
for i, idx := range idxs {
655-
xs[i] = xs[idx]
652+
func takeColumns[T any](xs []T, idxs []int) (out []T) {
653+
for _, idx := range idxs {
654+
out = append(out, xs[idx])
656655
}
657-
return xs[:len(idxs)]
656+
return
658657
}
659658

660659
func getColumnIndexes(names []string, header []string) ([]int, error) {

script_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package statedb
33
import (
44
"context"
55
"maps"
6+
"slices"
7+
"strings"
68
"testing"
79

810
"github.com/cilium/hive"
@@ -38,5 +40,68 @@ func TestScript(t *testing.T) {
3840
context.Background(), func() *script.Engine {
3941
return engine
4042
}, []string{}, "testdata/*.txtar")
43+
}
44+
45+
func TestHeaderLine(t *testing.T) {
46+
type retrieval struct {
47+
header string
48+
idxs []int
49+
}
50+
testCases := []struct {
51+
line string
52+
names []string
53+
pos []int
54+
get []retrieval
55+
}{
56+
{
57+
"Foo Bar ",
58+
[]string{"Foo", "Bar"},
59+
[]int{0, 6},
60+
[]retrieval{
61+
{"Foo", []int{0}},
62+
{"Bar", []int{1}},
63+
{"Bar Foo Bar", []int{1, 0, 1}},
64+
},
65+
},
66+
{
67+
"Foo Bar Quux",
68+
[]string{"Foo", "Bar", "Quux"},
69+
[]int{0, 4, 10},
70+
[]retrieval{
71+
{"Foo", []int{0}},
72+
{"Bar", []int{1}},
73+
{"Bar Foo", []int{1, 0}},
74+
{"Quux", []int{2}},
75+
{"Quux Foo", []int{2, 0}},
76+
},
77+
},
78+
}
79+
80+
for _, tc := range testCases {
81+
// Parse header line into names and positions
82+
names, pos := splitHeaderLine(tc.line)
83+
require.Equal(t, tc.names, names)
84+
require.Equal(t, tc.pos, pos)
4185

86+
// Split the header line with the parsed positions.
87+
header := splitByPositions(tc.line, pos)
88+
require.Equal(t, tc.names, header)
89+
90+
// Join the headers with the positions.
91+
line := joinByPositions(header, pos)
92+
require.Equal(t, strings.TrimRight(tc.line, " \t"), line)
93+
94+
// Test retrievals
95+
for _, r := range tc.get {
96+
names, pos = splitHeaderLine(r.header)
97+
idxs, err := getColumnIndexes(names, header)
98+
require.NoError(t, err)
99+
require.Equal(t, r.idxs, idxs)
100+
101+
row := slices.Clone(header)
102+
cols := takeColumns(row, idxs)
103+
line := joinByPositions(cols, pos)
104+
require.Equal(t, line, r.header)
105+
}
106+
}
42107
}

0 commit comments

Comments
 (0)