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  }