github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/columns/sort/sort_test.go (about) 1 // Copyright 2022-2023 The Inspektor Gadget authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sort 16 17 import ( 18 "fmt" 19 "math/rand" 20 "reflect" 21 "testing" 22 23 "github.com/inspektor-gadget/inspektor-gadget/pkg/columns" 24 ) 25 26 type testEmbedded struct { 27 EmbeddedInt int `column:"embeddedInt"` 28 } 29 30 type testEmbeddedPtr struct { 31 EmbeddedPtrInt int `column:"embeddedPtrInt"` 32 } 33 34 type testData struct { 35 testEmbedded 36 *testEmbeddedPtr 37 Int int `column:"int"` 38 Uint uint `column:"uint"` 39 String string `column:"string"` 40 Float32 float32 `column:"float32"` 41 Float64 float64 `column:"float64"` 42 Bool bool `column:"bool"` 43 Group string `column:"group"` 44 Extractor int `column:"extractor"` 45 } 46 47 func getTestCol(t *testing.T) *columns.Columns[testData] { 48 cols, err := columns.NewColumns[testData]() 49 if err != nil { 50 t.Errorf("Failed to initialize %v", err) 51 } 52 cols.MustSetExtractor("extractor", func(t *testData) any { 53 return fmt.Sprint(t.Extractor) 54 }) 55 cols.MustAddColumn(columns.Attributes{ 56 Name: "virtual_column", 57 }, func(*testData) any { 58 return "" 59 }) 60 61 return cols 62 } 63 64 func TestSorter(t *testing.T) { 65 testEntries := []*testData{ 66 nil, 67 {Int: 1, Uint: 2, String: "c", Float32: 3, Float64: 4, Group: "b", testEmbedded: testEmbedded{EmbeddedInt: 7}, testEmbeddedPtr: &testEmbeddedPtr{EmbeddedPtrInt: 7}, Extractor: 1}, 68 nil, 69 {Int: 2, Uint: 3, String: "d", Float32: 4, Float64: 5, Group: "b", testEmbedded: testEmbedded{EmbeddedInt: 6}, testEmbeddedPtr: &testEmbeddedPtr{EmbeddedPtrInt: 6}, Extractor: 2}, 70 nil, 71 {Int: 3, Uint: 4, String: "e", Float32: 5, Float64: 1, Group: "a", testEmbedded: testEmbedded{EmbeddedInt: 5}, testEmbeddedPtr: nil, Extractor: 3}, 72 nil, 73 {Int: 4, Uint: 5, String: "a", Float32: 1, Float64: 2, Group: "a", testEmbedded: testEmbedded{EmbeddedInt: 4}, testEmbeddedPtr: &testEmbeddedPtr{EmbeddedPtrInt: 4}, Extractor: 4}, 74 nil, 75 {Int: 5, Uint: 1, String: "b", Float32: 2, Float64: 3, Group: "c", testEmbedded: testEmbedded{EmbeddedInt: 3}, testEmbeddedPtr: &testEmbeddedPtr{EmbeddedPtrInt: 3}, Extractor: 5}, 76 nil, 77 } 78 79 // Using shuffle should cover all sorting paths 80 r := rand.New(rand.NewSource(0)) 81 82 cmap := getTestCol(t).GetColumnMap() 83 84 shuffle := func() { 85 r.Shuffle(len(testEntries), func(i, j int) { testEntries[i], testEntries[j] = testEntries[j], testEntries[i] }) 86 } 87 88 if !CanSortBy(cmap, []string{"uint"}) { 89 t.Errorf("expected sort to be able to sort by \"uint\" (struct field without custom extractor)") 90 } 91 if !CanSortBy(cmap, []string{"extractor"}) { 92 t.Errorf("expected sort to be able to sort by \"extractor\" (struct field with custom extractor)") 93 } 94 if CanSortBy(cmap, []string{"virtual_column"}) { 95 t.Errorf("expected sort to not be able to sort by \"virtual_column\" (virtual column)") 96 } 97 if CanSortBy(cmap, []string{"non_existent_column"}) { 98 t.Errorf("expected sort to not be able to sort by \"non_existent_column\" (column doesn't exist)") 99 } 100 101 shuffle() 102 SortEntries(cmap, testEntries, []string{"uint"}) 103 if testEntries[0].Uint != 1 { 104 t.Errorf("expected value to be 1") 105 } 106 107 shuffle() 108 SortEntries(cmap, testEntries, []string{"-uint"}) 109 if testEntries[0].Uint != 5 { 110 t.Errorf("expected value to be 5") 111 } 112 113 shuffle() 114 SortEntries(cmap, testEntries, []string{"int"}) 115 if testEntries[0].Int != 1 { 116 t.Errorf("expected value to be 1") 117 } 118 119 shuffle() 120 SortEntries(cmap, testEntries, []string{"-int"}) 121 if testEntries[0].Int != 5 { 122 t.Errorf("expected value to be 5") 123 } 124 125 shuffle() 126 SortEntries(cmap, testEntries, []string{"float32"}) 127 if testEntries[0].Float32 != 1 { 128 t.Errorf("expected value to be 1") 129 } 130 131 shuffle() 132 SortEntries(cmap, testEntries, []string{"-float32"}) 133 if testEntries[0].Float32 != 5 { 134 t.Errorf("expected value to be 5") 135 } 136 137 shuffle() 138 SortEntries(cmap, testEntries, []string{"float64"}) 139 if testEntries[0].Float64 != 1 { 140 t.Errorf("expected value to be 1") 141 } 142 143 shuffle() 144 SortEntries(cmap, testEntries, []string{"-float64"}) 145 if testEntries[0].Float64 != 5 { 146 t.Errorf("expected value to be 5") 147 } 148 149 shuffle() 150 SortEntries(cmap, testEntries, []string{"group", "string"}) 151 if testEntries[0].Group != "a" || testEntries[0].String != "a" { 152 t.Errorf("expected value to be a (group) and a (string)") 153 } 154 155 shuffle() 156 SortEntries(cmap, testEntries, []string{"-embeddedInt"}) 157 if testEntries[0].EmbeddedInt != 7 { 158 t.Errorf("expected embedded value to be a 7") 159 } 160 161 shuffle() 162 SortEntries(cmap, testEntries, []string{"-embeddedPtrInt"}) 163 if testEntries[0].EmbeddedPtrInt != 7 { 164 t.Errorf("expected embedded ptr value to be a 7") 165 } 166 167 shuffle() 168 SortEntries(cmap, testEntries, []string{"-extractor"}) 169 if testEntries[0].Extractor != 5 { 170 t.Errorf("expected value to be 5") 171 } 172 173 shuffle() 174 SortEntries(cmap, testEntries, []string{"string"}) 175 if testEntries[0].String != "a" { 176 t.Errorf("expected value to be a") 177 } 178 179 // Sort by unsupported column - should result in noop 180 SortEntries(cmap, testEntries, []string{"bool"}) 181 if testEntries[0].String != "a" { 182 t.Errorf("expected value to be a") 183 } 184 185 // Sort by invalid column - should result in noop 186 SortEntries(cmap, testEntries, []string{"invalid"}) 187 if testEntries[0].String != "a" { 188 t.Errorf("expected value to be a") 189 } 190 191 // Sort by empty column - should result in noop 192 SortEntries(cmap, testEntries, []string{""}) 193 if testEntries[0].String != "a" { 194 t.Errorf("expected value to be a") 195 } 196 197 // Sort nil array - should result in noop 198 SortEntries(cmap, nil, []string{""}) 199 } 200 201 func TestCanSortBy(t *testing.T) { 202 cmap := getTestCol(t).GetColumnMap() 203 204 if !CanSortBy(cmap, []string{"uint"}) { 205 t.Errorf("expected sort to be able to sort by \"uint\" (struct field without custom extractor)") 206 } 207 if !CanSortBy(cmap, []string{"extractor"}) { 208 t.Errorf("expected sort to be able to sort by \"extractor\" (struct field with custom extractor)") 209 } 210 if CanSortBy(cmap, []string{"virtual_column"}) { 211 t.Errorf("expected sort to not be able to sort by \"virtual_column\" (virtual column)") 212 } 213 if CanSortBy(cmap, []string{"non_existent_column"}) { 214 t.Errorf("expected sort to not be able to sort by \"non_existent_column\" (column doesn't exist)") 215 } 216 } 217 218 func TestFilterSortableColumns(t *testing.T) { 219 cmap := getTestCol(t).GetColumnMap() 220 221 valid, invalid := FilterSortableColumns(cmap, []string{"uint"}) 222 if !reflect.DeepEqual(valid, []string{"uint"}) || len(invalid) != 0 { 223 t.Errorf("expected FilterSortableColumns to return \"uint\" in the valid array (struct field without custom extractor)") 224 } 225 valid, invalid = FilterSortableColumns(cmap, []string{"extractor"}) 226 if !reflect.DeepEqual(valid, []string{"extractor"}) || len(invalid) != 0 { 227 t.Errorf("expected FilterSortableColumns to return \"extractor\" in the valid array (struct field with custom extractor)") 228 } 229 valid, invalid = FilterSortableColumns(cmap, []string{"virtual_column"}) 230 if len(valid) != 0 || !reflect.DeepEqual(invalid, []string{"virtual_column"}) { 231 t.Errorf("expected FilterSortableColumns to return \"virtual_column\" in the invalid array (virtual column)") 232 } 233 valid, invalid = FilterSortableColumns(cmap, []string{"non_existent_column"}) 234 if len(valid) != 0 || !reflect.DeepEqual(invalid, []string{"non_existent_column"}) { 235 t.Errorf("expected FilterSortableColumns to return \"non_existent_column\" in the invalid array (column doesn't exist)") 236 } 237 238 valid, _ = FilterSortableColumns(cmap, []string{"uint", "extractor"}) 239 if len(valid) != 2 || !reflect.DeepEqual(valid, []string{"uint", "extractor"}) { 240 t.Errorf("expected FilterSortableColumns to not change the ordering") 241 } 242 }