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 }