github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/diagnostic/memsize/deep_size_test.go (about)

     1  package memsize
     2  
     3  import (
     4  	"testing"
     5  	"unsafe"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  )
     9  
    10  var (
    11  	x             int64  = 5
    12  	pointerSize          = int(unsafe.Sizeof(&x))
    13  	sliceSize            = int(unsafe.Sizeof(make([]int64, 0)))
    14  	stringSize           = int(unsafe.Sizeof("abcde"))
    15  	mapSize              = int(unsafe.Sizeof(make(map[int32]int32)))
    16  	temper        Temper = nil
    17  	interfaceSize        = int(unsafe.Sizeof(temper))
    18  )
    19  
    20  type TestCase struct {
    21  	x        interface{}
    22  	expected int
    23  }
    24  
    25  func TestPrimitives(t *testing.T) {
    26  	var int8val int8 = 3
    27  	var uint8val uint8 = 3
    28  	var int16val int16 = -2
    29  	var uint16val uint16 = 2
    30  	var int32val int32 = -54
    31  	var uint32val uint32 = 54
    32  	var int64val int64 = -34
    33  	var uint64val uint64 = 34
    34  	var boolval bool = true
    35  	var float64val float64 = 3.29
    36  	var float32val float32 = 543.23
    37  	var int8ptr *int8
    38  	tests := []TestCase{
    39  		{nil, 0},
    40  		{&int64val, 8},
    41  		{&uint64val, 8},
    42  		{&int32val, 4},
    43  		{&uint32val, 4},
    44  		{&int16val, 2},
    45  		{&uint16val, 2},
    46  		{&int8val, 1},
    47  		{&uint8val, 1},
    48  		{&float64val, 8},
    49  		{&float32val, 4},
    50  		{&boolval, 1},
    51  		{int8ptr, 0},            // nil pointer
    52  		{&int8ptr, pointerSize}, // pointer pointer
    53  	}
    54  	runTests(tests, t)
    55  }
    56  
    57  func TestSlicesAndArray(t *testing.T) {
    58  	var int64val int64
    59  	var int64val2 int64
    60  	var int64ptr *int64
    61  	var int8slice []int8 = []int8{0, 1, 2, 3, 4, 5, 6}
    62  	var int8sliceB = int8slice[0:4]
    63  
    64  	type smallStruct struct { // size = 16 bytes
    65  		A, B int64
    66  	}
    67  
    68  	type smallPointerStruct struct { // size = 24 bytes + maybe 8 for ptr
    69  		A, B int64
    70  		APtr *int64
    71  	}
    72  
    73  	type complexStruct struct { // size = 40 bytes + maybe 8 for ptr
    74  		T smallStruct
    75  		Y smallPointerStruct
    76  	}
    77  
    78  	type structWithZeroArray struct { // size = 40 bytes + maybe 8 for ptr
    79  		M [0]int64
    80  		W complexStruct
    81  	}
    82  
    83  	tests := []TestCase{
    84  		{&[]int64{1, 2, 3}, sliceSize + 8*3},
    85  		{&[]*int64{int64ptr, &int64val}, sliceSize + pointerSize*2 + 8},
    86  		{&[]*int64{&int64val, &int64val}, sliceSize + pointerSize*2 + 8},
    87  		{&[]*int64{&int64val2, &int64val}, sliceSize + pointerSize*2 + 2*8},
    88  		{&[]smallStruct{{}, {}}, sliceSize + 16*2},
    89  		{&[]complexStruct{{}, {Y: smallPointerStruct{APtr: &int64val}}}, sliceSize + 40*2 + 8},
    90  		{&[][3]int64{{1, 2, 3}, {4, 5, 6}}, sliceSize + 2*24},
    91  		{&[...]int64{1, 2, 3}, 8 * 3},
    92  		{&[0]int64{}, 0},
    93  		{&[]structWithZeroArray{{}, {}}, sliceSize + 2*40},
    94  		{&[...]smallPointerStruct{{A: 1, B: 1, APtr: &int64val}, {A: 1, B: 1, APtr: nil}}, 2*24 + 8},
    95  		{&int8sliceB, sliceSize + 4},
    96  		{&[][]int8{int8slice[0:4], int8slice[0:4]}, 3*sliceSize + 4}, // overlapping memory locations
    97  		{&[][]int8{int8slice[0:4], int8slice[2:6]}, 3*sliceSize + 6}, // overlapping memory locations
    98  	}
    99  	runTests(tests, t)
   100  
   101  }
   102  
   103  func TestStrings(t *testing.T) {
   104  	var emptyString = ""
   105  	var abcdefgString = "abcdefg"
   106  	tests := []TestCase{
   107  		{&emptyString, stringSize},
   108  		{&abcdefgString, stringSize + 7},
   109  		{&[]string{"abcd", "defg"}, sliceSize + 2*stringSize + 2*4}, // no string interning
   110  		{&[]string{"abcd", "abcd"}, sliceSize + 2*stringSize + 4},   // string interning
   111  	}
   112  	runTests(tests, t)
   113  }
   114  
   115  func TestMap(t *testing.T) {
   116  	var int8val int8
   117  	tests := []TestCase{
   118  		{&map[int64]int64{2: 3}, mapSize + 8 + 8},
   119  		{&map[string]int32{"abc": 3}, mapSize + stringSize + 3 + 4},
   120  		{&map[string]*int8{"abc": &int8val, "def": &int8val}, mapSize + 2*stringSize + 2*3 + 2*pointerSize + 1},
   121  	}
   122  	runTests(tests, t)
   123  }
   124  
   125  type Temper interface {
   126  	Temp()
   127  }
   128  
   129  type TemperMock struct {
   130  	A int64
   131  }
   132  
   133  func (TemperMock) Temp() {}
   134  
   135  func TestStructs(t *testing.T) {
   136  	type struct1 struct {
   137  		A int64
   138  		B float64
   139  	}
   140  
   141  	type struct2 struct {
   142  		A      int64
   143  		B      float64
   144  		temper Temper
   145  	}
   146  
   147  	type recursiveType struct {
   148  		A   int64
   149  		ptr *recursiveType
   150  	}
   151  
   152  	type nestedType1 struct { // 8 bytes + maybe 8 bytes
   153  		A *int64
   154  	}
   155  	type nestedType2 struct { // 8 bytes + maybe 8 bytes
   156  		X nestedType1
   157  	}
   158  	type nestedType3 struct { // 8 bytes + maybe 8 bytes
   159  		Y nestedType2
   160  	}
   161  
   162  	type structWithZeroArray struct { // 8 bytes + maybe 8 bytes
   163  		X [0]int64
   164  		Y nestedType2
   165  	}
   166  
   167  	var int64val int64
   168  	var recursiveVar1 = recursiveType{A: 1}
   169  	var recursiveVar2 = recursiveType{A: 2}
   170  	recursiveVar1.ptr = &recursiveVar2
   171  	recursiveVar2.ptr = &recursiveVar1
   172  
   173  	tests := []TestCase{
   174  		{&nestedType3{Y: nestedType2{X: nestedType1{A: &int64val}}}, pointerSize + 8},
   175  		{&struct1{1, 1}, 16},
   176  		{&struct2{1, 1, TemperMock{}}, 16 + interfaceSize + 8},
   177  		{&struct2{1, 1, nil}, 16 + interfaceSize},
   178  		{&recursiveVar1, 2 * (8 + pointerSize)},
   179  		{&structWithZeroArray{Y: nestedType2{}, X: [0]int64{}}, 8},
   180  	}
   181  
   182  	runTests(tests, t)
   183  }
   184  
   185  func TestCornerCaseTypes(t *testing.T) {
   186  	var chanVar chan int
   187  	tests := []TestCase{
   188  		{&struct{ A func(x int) int }{A: func(x int) int { return x + 1 }}, pointerSize},
   189  		{&chanVar, pointerSize},
   190  	}
   191  	runTests(tests, t)
   192  }
   193  
   194  func TestPanicOnNonNil(t *testing.T) {
   195  	tests := []interface{}{
   196  		"abc",
   197  		5,
   198  		3.5,
   199  		struct {
   200  			A int
   201  			B int
   202  		}{A: 5, B: 5},
   203  	}
   204  	for i := range tests {
   205  		assert.Panics(t, func() { DeepSize(tests[i]) }, "should panic")
   206  	}
   207  }
   208  
   209  func runTests(tests []TestCase, t *testing.T) {
   210  	for i, test := range tests {
   211  		if got := DeepSize(test.x); got != test.expected {
   212  			t.Errorf("test %d: got %d, expected %d", i, got, test.expected)
   213  		}
   214  	}
   215  }