github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/columns/columninfo_test.go (about)

     1  // Copyright 2022 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 columns
    16  
    17  import (
    18  	"reflect"
    19  	"testing"
    20  
    21  	"github.com/inspektor-gadget/inspektor-gadget/pkg/columns/ellipsis"
    22  )
    23  
    24  func expectColumnsSuccess[T any](t *testing.T, options ...Option) *Columns[T] {
    25  	cols, err := NewColumns[T](options...)
    26  	if err != nil {
    27  		t.Fatalf("Failed to initialize: %v", err)
    28  	}
    29  	return cols
    30  }
    31  
    32  func expectColumnsFail[T any](t *testing.T, name string, options ...Option) {
    33  	t.Run(name, func(t *testing.T) {
    34  		_, err := NewColumns[T](options...)
    35  		if err == nil {
    36  			t.Errorf("Succeeded to initialize but expected error")
    37  		}
    38  	})
    39  }
    40  
    41  func expectColumn[T any](t *testing.T, cols *Columns[T], columnName string) *Column[T] {
    42  	col, ok := cols.GetColumn(columnName)
    43  	if !ok {
    44  		t.Fatalf("Expected column with name %q", columnName)
    45  	}
    46  	return col
    47  }
    48  
    49  func expectColumnValue[T any](t *testing.T, col *Column[T], fieldName string, expectedValue interface{}) {
    50  	columnValue := reflect.ValueOf(col).Elem()
    51  	fieldValue := columnValue.FieldByName(fieldName)
    52  	if !fieldValue.IsValid() {
    53  		t.Errorf("Expected field %q", fieldName)
    54  		return
    55  	}
    56  	if fieldValue.Interface() != expectedValue {
    57  		t.Errorf("Expected field %q to equal %+v, got %+v", fieldName, expectedValue, fieldValue.Interface())
    58  	}
    59  }
    60  
    61  func TestColumnsInvalid(t *testing.T) {
    62  	type testFail1 struct {
    63  		Unknown string `column:"left,unknown"`
    64  	}
    65  	type testFail2 struct {
    66  		Unknown1 string `column:"unknown"`
    67  		Unknown2 string `column:"unknown"`
    68  	}
    69  	type testFail3 struct {
    70  		testFail2
    71  	}
    72  	expectColumnsFail[testFail1](t, "unknown parameter")
    73  	expectColumnsFail[testFail2](t, "double name")
    74  	expectColumnsFail[testFail3](t, "nested double name")
    75  }
    76  
    77  func TestColumnsAlign(t *testing.T) {
    78  	type testSuccess1 struct {
    79  		AlignLeft  string `column:"left,align:left"`
    80  		AlignRight string `column:"right,align:right"`
    81  	}
    82  
    83  	cols := expectColumnsSuccess[testSuccess1](t)
    84  	expectColumnValue(t, expectColumn(t, cols, "left"), "Alignment", AlignLeft)
    85  	expectColumnValue(t, expectColumn(t, cols, "right"), "Alignment", AlignRight)
    86  
    87  	expectColumnsFail[struct {
    88  		Field string `column:"fail,align"`
    89  	}](t, "missing parameter")
    90  	expectColumnsFail[struct {
    91  		Field string `column:"fail,align:"`
    92  	}](t, "empty parameter")
    93  	expectColumnsFail[struct {
    94  		Field string `column:"fail,align:foo"`
    95  	}](t, "invalid parameter")
    96  	expectColumnsFail[struct {
    97  		Field string `column:"fail,align:left:bar"`
    98  	}](t, "double parameter")
    99  }
   100  
   101  func TestColumnsEllipsis(t *testing.T) {
   102  	type testSuccess1 struct {
   103  		EllipsisEmpty      string `column:"empty,ellipsis"`
   104  		EllipsisEmptyColon string `column:"emptyColon,ellipsis:"`
   105  		EllipsisNone       string `column:"none,ellipsis:none"`
   106  		EllipsisStart      string `column:"start,ellipsis:start"`
   107  		EllipsisEnd        string `column:"end,ellipsis:end"`
   108  		EllipsisMiddle     string `column:"middle,ellipsis:middle"`
   109  	}
   110  
   111  	cols := expectColumnsSuccess[testSuccess1](t)
   112  	expectColumnValue(t, expectColumn(t, cols, "empty"), "EllipsisType", cols.options.DefaultEllipsis)
   113  	expectColumnValue(t, expectColumn(t, cols, "emptyColon"), "EllipsisType", cols.options.DefaultEllipsis)
   114  	expectColumnValue(t, expectColumn(t, cols, "none"), "EllipsisType", ellipsis.None)
   115  	expectColumnValue(t, expectColumn(t, cols, "start"), "EllipsisType", ellipsis.Start)
   116  	expectColumnValue(t, expectColumn(t, cols, "end"), "EllipsisType", ellipsis.End)
   117  	expectColumnValue(t, expectColumn(t, cols, "middle"), "EllipsisType", ellipsis.Middle)
   118  
   119  	expectColumnsFail[struct {
   120  		Field string `column:"fail,ellipsis:foo"`
   121  	}](t, "invalid parameter")
   122  	expectColumnsFail[struct {
   123  		Field string `column:"fail,ellipsis:left:bar"`
   124  	}](t, "double parameter")
   125  }
   126  
   127  func TestColumnsFixed(t *testing.T) {
   128  	type testSuccess1 struct {
   129  		Field string `column:"field,fixed"`
   130  	}
   131  
   132  	cols := expectColumnsSuccess[testSuccess1](t)
   133  	expectColumnValue(t, expectColumn(t, cols, "field"), "FixedWidth", true)
   134  
   135  	expectColumnsFail[struct {
   136  		Field string `column:"fail,fixed:foo"`
   137  	}](t, "invalid parameter")
   138  }
   139  
   140  func TestColumnsGroup(t *testing.T) {
   141  	type testSuccess1 struct {
   142  		FieldInt     int64   `column:"int,group:sum"`
   143  		FieldUint    int64   `column:"uint,group:sum"`
   144  		FieldFloat32 float32 `column:"float32,group:sum"`
   145  		FieldFloat64 float64 `column:"float64,group:sum"`
   146  	}
   147  
   148  	cols := expectColumnsSuccess[testSuccess1](t)
   149  	expectColumnValue(t, expectColumn(t, cols, "int"), "GroupType", GroupTypeSum)
   150  	expectColumnValue(t, expectColumn(t, cols, "uint"), "GroupType", GroupTypeSum)
   151  	expectColumnValue(t, expectColumn(t, cols, "float32"), "GroupType", GroupTypeSum)
   152  	expectColumnValue(t, expectColumn(t, cols, "float64"), "GroupType", GroupTypeSum)
   153  
   154  	expectColumnsFail[struct {
   155  		Field int64 `column:"fail,group"`
   156  	}](t, "missing parameter")
   157  	expectColumnsFail[struct {
   158  		Field int64 `column:"fail,group:"`
   159  	}](t, "empty parameter")
   160  	expectColumnsFail[struct {
   161  		Field int64 `column:"fail,group:foo"`
   162  	}](t, "invalid parameter")
   163  	expectColumnsFail[struct {
   164  		Field int64 `column:"fail,group:sum:bar"`
   165  	}](t, "double parameter")
   166  	expectColumnsFail[struct {
   167  		Field string `column:"fail,group:sum"`
   168  	}](t, "wrong type")
   169  }
   170  
   171  func TestColumnsHide(t *testing.T) {
   172  	type testSuccess1 struct {
   173  		Field string `column:"field,hide"`
   174  	}
   175  
   176  	cols := expectColumnsSuccess[testSuccess1](t)
   177  	expectColumnValue(t, expectColumn(t, cols, "field"), "Visible", false)
   178  
   179  	expectColumnsFail[struct {
   180  		Field string `column:"fail,hide:foo"`
   181  	}](t, "invalid parameter")
   182  }
   183  
   184  func TestColumnsOrder(t *testing.T) {
   185  	type testSuccess1 struct {
   186  		FieldWidth int64 `column:"int,order:4"`
   187  	}
   188  
   189  	cols := expectColumnsSuccess[testSuccess1](t)
   190  	expectColumnValue(t, expectColumn(t, cols, "int"), "Order", 4)
   191  
   192  	expectColumnsFail[struct {
   193  		Field int64 `column:"fail,order"`
   194  	}](t, "missing parameter")
   195  	expectColumnsFail[struct {
   196  		Field int64 `column:"fail,order:"`
   197  	}](t, "empty parameter")
   198  	expectColumnsFail[struct {
   199  		Field int64 `column:"fail,order:foo"`
   200  	}](t, "invalid parameter")
   201  	expectColumnsFail[struct {
   202  		Field int64 `column:"fail,order:sum:bar"`
   203  	}](t, "double parameter")
   204  }
   205  
   206  func TestColumnsPrecision(t *testing.T) {
   207  	type testSuccess1 struct {
   208  		Float32 float32 `column:"float32,precision:4"`
   209  		Float64 float64 `column:"float64,precision:4"`
   210  	}
   211  
   212  	cols := expectColumnsSuccess[testSuccess1](t)
   213  	expectColumnValue(t, expectColumn(t, cols, "float32"), "Precision", 4)
   214  	expectColumnValue(t, expectColumn(t, cols, "float64"), "Precision", 4)
   215  
   216  	expectColumnsFail[struct {
   217  		Field1 float32 `column:"fail,precision"`
   218  	}](t, "float32: missing parameter")
   219  	expectColumnsFail[struct {
   220  		Field float32 `column:"fail,precision:"`
   221  	}](t, "float32: empty parameter")
   222  	expectColumnsFail[struct {
   223  		Field float32 `column:"fail,precision:foo"`
   224  	}](t, "float32: invalid parameter")
   225  	expectColumnsFail[struct {
   226  		Field float32 `column:"fail,precision:-2"`
   227  	}](t, "float32: double parameter")
   228  	expectColumnsFail[struct {
   229  		Field1 float64 `column:"fail,precision"`
   230  	}](t, "float64: missing parameter")
   231  	expectColumnsFail[struct {
   232  		Field float64 `column:"fail,precision:"`
   233  	}](t, "float64: empty parameter")
   234  	expectColumnsFail[struct {
   235  		Field float64 `column:"fail,precision:foo"`
   236  	}](t, "float64: invalid parameter")
   237  	expectColumnsFail[struct {
   238  		Field float64 `column:"fail,precision:-2"`
   239  	}](t, "float64: double parameter")
   240  	expectColumnsFail[struct {
   241  		Field string `column:"fail,precision:2"`
   242  	}](t, "invalid field")
   243  }
   244  
   245  func TestColumnsWidth(t *testing.T) {
   246  	type testSuccess1 struct {
   247  		FieldWidth     int64 `column:"int,width:4"`
   248  		FieldWidthType int64 `column:"intType,width:type"`
   249  	}
   250  
   251  	cols := expectColumnsSuccess[testSuccess1](t)
   252  	expectColumnValue(t, expectColumn(t, cols, "int"), "Width", 4)
   253  	expectColumnValue(t, expectColumn(t, cols, "intType"), "Width", MaxCharsInt64)
   254  
   255  	expectColumnsFail[struct {
   256  		Field int64 `column:"fail,width"`
   257  	}](t, "missing parameter")
   258  	expectColumnsFail[struct {
   259  		Field int64 `column:"fail,width:"`
   260  	}](t, "empty parameter")
   261  	expectColumnsFail[struct {
   262  		Field int64 `column:"fail,width:foo"`
   263  	}](t, "invalid parameter")
   264  	expectColumnsFail[struct {
   265  		Field int64 `column:"fail,width:sum:bar"`
   266  	}](t, "double parameter")
   267  }
   268  
   269  func TestColumnsMaxWidth(t *testing.T) {
   270  	type testSuccess1 struct {
   271  		FieldMaxWidth     int64 `column:"int,maxWidth:4"`
   272  		FieldMaxWidthType int64 `column:"intType,maxWidth:type"`
   273  	}
   274  
   275  	cols := expectColumnsSuccess[testSuccess1](t)
   276  
   277  	expectColumnValue(t, expectColumn(t, cols, "int"), "MaxWidth", 4)
   278  	expectColumnValue(t, expectColumn(t, cols, "intType"), "MaxWidth", MaxCharsInt64)
   279  
   280  	expectColumnsFail[struct {
   281  		Field int64 `column:"fail,maxWidth"`
   282  	}](t, "missing parameter")
   283  	expectColumnsFail[struct {
   284  		Field int64 `column:"fail,maxWidth:"`
   285  	}](t, "empty parameter")
   286  	expectColumnsFail[struct {
   287  		Field int64 `column:"fail,maxWidth:foo"`
   288  	}](t, "invalid parameter")
   289  	expectColumnsFail[struct {
   290  		Field int64 `column:"fail,maxWidth:sum:bar"`
   291  	}](t, "double parameter")
   292  }
   293  
   294  func TestColumnsMinWidth(t *testing.T) {
   295  	type testSuccess1 struct {
   296  		FieldMinWidth     int64 `column:"int,minWidth:4"`
   297  		FieldMaxWidthType int64 `column:"intType,minWidth:type"`
   298  	}
   299  
   300  	cols := expectColumnsSuccess[testSuccess1](t)
   301  	expectColumnValue(t, expectColumn(t, cols, "int"), "MinWidth", 4)
   302  	expectColumnValue(t, expectColumn(t, cols, "intType"), "MinWidth", MaxCharsInt64)
   303  
   304  	expectColumnsFail[struct {
   305  		Field int64 `column:"fail,minWidth"`
   306  	}](t, "missing parameter")
   307  	expectColumnsFail[struct {
   308  		Field int64 `column:"fail,minWidth:"`
   309  	}](t, "empty parameter")
   310  	expectColumnsFail[struct {
   311  		Field int64 `column:"fail,minWidth:foo"`
   312  	}](t, "invalid parameter")
   313  	expectColumnsFail[struct {
   314  		Field int64 `column:"fail,minWidth:sum:bar"`
   315  	}](t, "double parameter")
   316  }
   317  
   318  func TestColumnsWidthFromType(t *testing.T) {
   319  	type testSuccess1 struct {
   320  		Int8   int8   `column:",minWidth:type,maxWidth:type,width:type"`
   321  		Int16  int16  `column:",minWidth:type,maxWidth:type,width:type"`
   322  		Int32  int32  `column:",minWidth:type,maxWidth:type,width:type"`
   323  		Int64  int64  `column:",minWidth:type,maxWidth:type,width:type"`
   324  		Uint8  uint8  `column:",minWidth:type,maxWidth:type,width:type"`
   325  		Uint16 uint16 `column:",minWidth:type,maxWidth:type,width:type"`
   326  		Uint32 uint32 `column:",minWidth:type,maxWidth:type,width:type"`
   327  		Uint64 uint64 `column:",minWidth:type,maxWidth:type,width:type"`
   328  		Bool   bool   `column:",minWidth:type,maxWidth:type,width:type"`
   329  	}
   330  
   331  	cols := expectColumnsSuccess[testSuccess1](t)
   332  
   333  	col := expectColumn(t, cols, "int8")
   334  	expectColumnValue(t, col, "Width", MaxCharsInt8)
   335  	expectColumnValue(t, col, "MinWidth", MaxCharsInt8)
   336  	expectColumnValue(t, col, "MaxWidth", MaxCharsInt8)
   337  
   338  	col = expectColumn(t, cols, "int16")
   339  	expectColumnValue(t, col, "Width", MaxCharsInt16)
   340  	expectColumnValue(t, col, "MinWidth", MaxCharsInt16)
   341  	expectColumnValue(t, col, "MaxWidth", MaxCharsInt16)
   342  
   343  	col = expectColumn(t, cols, "int32")
   344  	expectColumnValue(t, col, "Width", MaxCharsInt32)
   345  	expectColumnValue(t, col, "MinWidth", MaxCharsInt32)
   346  	expectColumnValue(t, col, "MaxWidth", MaxCharsInt32)
   347  
   348  	col = expectColumn(t, cols, "int64")
   349  	expectColumnValue(t, col, "Width", MaxCharsInt64)
   350  	expectColumnValue(t, col, "MinWidth", MaxCharsInt64)
   351  	expectColumnValue(t, col, "MaxWidth", MaxCharsInt64)
   352  
   353  	col = expectColumn(t, cols, "uint8")
   354  	expectColumnValue(t, col, "Width", MaxCharsUint8)
   355  	expectColumnValue(t, col, "MinWidth", MaxCharsUint8)
   356  	expectColumnValue(t, col, "MaxWidth", MaxCharsUint8)
   357  
   358  	col = expectColumn(t, cols, "uint16")
   359  	expectColumnValue(t, col, "Width", MaxCharsUint16)
   360  	expectColumnValue(t, col, "MinWidth", MaxCharsUint16)
   361  	expectColumnValue(t, col, "MaxWidth", MaxCharsUint16)
   362  
   363  	col = expectColumn(t, cols, "uint32")
   364  	expectColumnValue(t, col, "Width", MaxCharsUint32)
   365  	expectColumnValue(t, col, "MinWidth", MaxCharsUint32)
   366  	expectColumnValue(t, col, "MaxWidth", MaxCharsUint32)
   367  
   368  	col = expectColumn(t, cols, "uint64")
   369  	expectColumnValue(t, col, "Width", MaxCharsUint64)
   370  	expectColumnValue(t, col, "MinWidth", MaxCharsUint64)
   371  	expectColumnValue(t, col, "MaxWidth", MaxCharsUint64)
   372  
   373  	col = expectColumn(t, cols, "bool")
   374  	expectColumnValue(t, col, "Width", MaxCharsBool)
   375  	expectColumnValue(t, col, "MinWidth", MaxCharsBool)
   376  	expectColumnValue(t, col, "MaxWidth", MaxCharsBool)
   377  
   378  	expectColumnsFail[struct {
   379  		String string `column:",minWidth:type,maxWidth:type,width:type"`
   380  	}](t, "invalid field type")
   381  }
   382  
   383  func TestWithoutColumnTag(t *testing.T) {
   384  	type Main struct {
   385  		StringField string
   386  		IntField    int
   387  	}
   388  
   389  	cols := expectColumnsSuccess[Main](t, WithRequireColumnDefinition(false))
   390  
   391  	expectColumn(t, cols, "StringField")
   392  }
   393  
   394  func TestColumnFilters(t *testing.T) {
   395  	type Embedded struct {
   396  		EmbeddedString string `column:"embeddedString" columnTags:"test"`
   397  	}
   398  	type Main struct {
   399  		Embedded
   400  		MainString string `column:"mainString" columnTags:"test2"`
   401  		NoTags     string `column:"noTags"`
   402  	}
   403  
   404  	cols := expectColumnsSuccess[Main](t)
   405  
   406  	expectColumn := func(columnName string, name string, filters ...ColumnFilter) {
   407  		t.Run(name, func(t *testing.T) {
   408  			colMap := cols.GetColumnMap(filters...)
   409  			if _, ok := colMap.GetColumn(columnName); !ok {
   410  				t.Errorf("Expected column %q to exist after applying filters", columnName)
   411  			}
   412  		})
   413  	}
   414  
   415  	expectColumn("embeddedString", "embedded or WithTag(test)", Or(WithEmbedded(true), WithTag("test")))
   416  	expectColumn("mainString", "not embedded or WithoutTag(test)", Or(WithEmbedded(false), WithoutTag("test")))
   417  	expectColumn("mainString", "WithTags(test2) and WithoutTags(test)", And(WithTags([]string{"test2"}), WithoutTags([]string{"test"})))
   418  	expectColumn("mainString", "WithTags(test2) and WithoutTags(test)", And(WithTags([]string{"test2"}), WithoutTags([]string{"test"})))
   419  
   420  	orderedColumns := cols.GetOrderedColumns(WithoutTags([]string{"test"})) // missing path
   421  	if len(orderedColumns) != 2 || orderedColumns[0].Name != "mainString" {
   422  		t.Errorf("Expected a mainString column after getting ordered columns using filters")
   423  	}
   424  
   425  	orderedColumns = cols.GetOrderedColumns(WithNoTags())
   426  	if len(orderedColumns) != 1 || orderedColumns[0].Name != "noTags" {
   427  		t.Errorf("Expected a noTags column after getting ordered columns using filters")
   428  	}
   429  }
   430  
   431  func TestColumnMatcher(t *testing.T) {
   432  	type Embedded struct {
   433  		EmbeddedString string `column:"embeddedString" columnTags:"test"`
   434  	}
   435  	type Main struct {
   436  		Embedded
   437  		MainString string `column:"mainString" columnTags:"test2"`
   438  	}
   439  
   440  	cols := expectColumnsSuccess[Main](t)
   441  
   442  	c := expectColumn(t, cols, "embeddedString")
   443  	if !c.IsEmbedded() {
   444  		t.Errorf("Expected the embedded field to be identified as embedded")
   445  	}
   446  	if !c.HasTag("test") {
   447  		t.Errorf("Expected the embedded field to have tag 'test'")
   448  	}
   449  	if c.HasTag("test2") {
   450  		t.Errorf("Didn't expect the embedded field to have tag 'test2'")
   451  	}
   452  
   453  	c = expectColumn(t, cols, "mainString")
   454  	if c.IsEmbedded() {
   455  		t.Errorf("Expected mainString to not be identified as embedded")
   456  	}
   457  	if !c.HasTag("test2") {
   458  		t.Errorf("Expected mainString to have tag 'test2'")
   459  	}
   460  	if c.HasTag("test") {
   461  		t.Errorf("Didn't expect mainString to have tag 'test'")
   462  	}
   463  }
   464  
   465  func TestColumnTemplates(t *testing.T) {
   466  	if RegisterTemplate("", "width:789") == nil {
   467  		t.Errorf("Expected error because of empty name")
   468  	}
   469  	if RegisterTemplate("demo", "") == nil {
   470  		t.Errorf("Expected error because of empty value")
   471  	}
   472  
   473  	if err := RegisterTemplate("numbers", "width:123"); err != nil {
   474  		t.Errorf("Expected success, got %v", err)
   475  	}
   476  
   477  	type testSuccess1 struct {
   478  		Int16 int16 `column:",template:numbers"`
   479  		Int32 int32 `column:",template:numbers,width:5"`
   480  		Int64 int64 `column:",template:numbers,hide"`
   481  	}
   482  
   483  	cols := expectColumnsSuccess[testSuccess1](t)
   484  
   485  	expectColumnValue(t, expectColumn(t, cols, "int16"), "Width", 123)
   486  	expectColumnValue(t, expectColumn(t, cols, "int32"), "Width", 5)
   487  	expectColumnValue(t, expectColumn(t, cols, "int64"), "Width", 123)
   488  	expectColumnValue(t, expectColumn(t, cols, "int64"), "Visible", false)
   489  
   490  	expectColumnsFail[struct {
   491  		String string `column:",template"`
   492  	}](t, "not template name given")
   493  
   494  	expectColumnsFail[struct {
   495  		String string `column:",template:foobar"`
   496  	}](t, "trying to use non-existing template")
   497  }
   498  
   499  func TestColumnTemplatesRegisterExisting(t *testing.T) {
   500  	t.Run("untyped", func(t *testing.T) {
   501  		defer func() {
   502  			if r := recover(); r == nil {
   503  				t.Errorf("expected panic")
   504  			}
   505  		}()
   506  		MustRegisterTemplate("abc", "width:123")
   507  		MustRegisterTemplate("abc", "width:123")
   508  	})
   509  }