github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/roaringset/layers_test.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package roaringset 13 14 import ( 15 "testing" 16 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 "github.com/weaviate/sroar" 20 ) 21 22 func Test_BitmapLayers_Flatten(t *testing.T) { 23 type inputSegment struct { 24 additions []uint64 25 deletions []uint64 26 } 27 28 type test struct { 29 name string 30 inputs []inputSegment 31 expectedContained []uint64 32 expectedNotContained []uint64 33 } 34 35 tests := []test{ 36 { 37 name: "no inputs", 38 inputs: nil, 39 expectedContained: nil, 40 expectedNotContained: nil, 41 }, 42 { 43 name: "single segment", 44 inputs: []inputSegment{ 45 { 46 additions: []uint64{4, 5}, 47 }, 48 }, 49 expectedContained: []uint64{4, 5}, 50 expectedNotContained: nil, 51 }, 52 { 53 name: "three segments, only additions", 54 inputs: []inputSegment{ 55 { 56 additions: []uint64{4, 5}, 57 }, 58 { 59 additions: []uint64{5, 6}, 60 }, 61 { 62 additions: []uint64{6, 7, 8}, 63 }, 64 }, 65 expectedContained: []uint64{4, 5, 6, 7, 8}, 66 expectedNotContained: nil, 67 }, 68 { 69 name: "two segments, including a delete", 70 inputs: []inputSegment{ 71 { 72 additions: []uint64{4, 5}, 73 }, 74 { 75 additions: []uint64{5, 6}, 76 deletions: []uint64{4}, 77 }, 78 }, 79 expectedContained: []uint64{5, 6}, 80 expectedNotContained: []uint64{4}, 81 }, 82 { 83 name: "three segments, including a delete, and a re-add", 84 inputs: []inputSegment{ 85 { 86 additions: []uint64{3, 4, 5}, 87 }, 88 { 89 additions: []uint64{6}, 90 deletions: []uint64{4, 5}, 91 }, 92 { 93 additions: []uint64{5}, 94 }, 95 }, 96 expectedContained: []uint64{3, 5, 6}, 97 expectedNotContained: []uint64{4}, 98 }, 99 } 100 101 for _, test := range tests { 102 t.Run(test.name, func(t *testing.T) { 103 input := make(BitmapLayers, len(test.inputs)) 104 for i, inp := range test.inputs { 105 input[i].Additions = NewBitmap(inp.additions...) 106 input[i].Deletions = NewBitmap(inp.deletions...) 107 } 108 109 res := input.Flatten() 110 for _, x := range test.expectedContained { 111 assert.True(t, res.Contains(x)) 112 } 113 114 for _, x := range test.expectedNotContained { 115 assert.False(t, res.Contains(x)) 116 } 117 }) 118 } 119 } 120 121 func Test_BitmapLayers_Merge(t *testing.T) { 122 type inputSegment struct { 123 additions []uint64 124 deletions []uint64 125 } 126 127 type test struct { 128 name string 129 inputs []inputSegment 130 expectedAdditions []uint64 131 expectedDeletions []uint64 132 expectErr bool 133 } 134 135 tests := []test{ 136 { 137 name: "no inputs - should error", 138 inputs: nil, 139 expectedAdditions: nil, 140 expectedDeletions: nil, 141 expectErr: true, 142 }, 143 { 144 name: "single layer - should error", 145 inputs: []inputSegment{ 146 { 147 additions: []uint64{4, 5}, 148 }, 149 }, 150 expectedAdditions: nil, 151 expectedDeletions: nil, 152 expectErr: true, 153 }, 154 { 155 name: "three layers - should error", 156 inputs: []inputSegment{ 157 { 158 additions: []uint64{4, 5}, 159 }, 160 { 161 additions: []uint64{4, 5}, 162 }, 163 { 164 additions: []uint64{4, 5}, 165 }, 166 }, 167 expectedAdditions: nil, 168 expectedDeletions: nil, 169 expectErr: true, 170 }, 171 { 172 name: "two layers, only additions", 173 inputs: []inputSegment{ 174 { 175 additions: []uint64{4, 5}, 176 }, 177 { 178 additions: []uint64{5, 6, 7}, 179 }, 180 }, 181 expectedAdditions: []uint64{4, 5, 6, 7}, 182 expectedDeletions: nil, 183 }, 184 { 185 name: "additions and deletions without overlap", 186 inputs: []inputSegment{ 187 { 188 additions: []uint64{4, 5}, 189 deletions: []uint64{1, 2}, 190 }, 191 { 192 additions: []uint64{5, 6, 7}, 193 deletions: []uint64{2, 3}, 194 }, 195 }, 196 expectedAdditions: []uint64{4, 5, 6, 7}, 197 expectedDeletions: []uint64{1, 2, 3}, 198 }, 199 { 200 name: "previously deleted element, re-added", 201 inputs: []inputSegment{ 202 { 203 additions: []uint64{}, 204 deletions: []uint64{1, 2}, 205 }, 206 { 207 additions: []uint64{2}, 208 deletions: []uint64{}, 209 }, 210 }, 211 expectedAdditions: []uint64{2}, 212 expectedDeletions: []uint64{1}, 213 }, 214 { 215 name: "previously added element deleted later", 216 inputs: []inputSegment{ 217 { 218 additions: []uint64{3, 4}, 219 deletions: []uint64{}, 220 }, 221 { 222 additions: []uint64{}, 223 deletions: []uint64{3}, 224 }, 225 }, 226 expectedAdditions: []uint64{4}, 227 expectedDeletions: []uint64{3}, 228 }, 229 } 230 231 for _, test := range tests { 232 t.Run(test.name, func(t *testing.T) { 233 input := make(BitmapLayers, len(test.inputs)) 234 for i, inp := range test.inputs { 235 input[i].Additions = NewBitmap(inp.additions...) 236 input[i].Deletions = NewBitmap(inp.deletions...) 237 } 238 239 res, err := input.Merge() 240 if test.expectErr { 241 require.NotNil(t, err) 242 return 243 } else { 244 require.Nil(t, err) 245 } 246 for _, x := range test.expectedAdditions { 247 assert.True(t, res.Additions.Contains(x)) 248 } 249 250 for _, x := range test.expectedDeletions { 251 assert.True(t, res.Deletions.Contains(x)) 252 } 253 254 intersect := sroar.And(res.Additions, res.Deletions) 255 assert.True(t, intersect.IsEmpty(), 256 "verify that additions and deletions never intersect") 257 }) 258 } 259 } 260 261 func Test_BitmapLayer_Clone(t *testing.T) { 262 t.Run("cloning empty BitmapLayer", func(t *testing.T) { 263 layerEmpty := BitmapLayer{} 264 265 cloned := layerEmpty.Clone() 266 267 assert.Nil(t, cloned.Additions) 268 assert.Nil(t, cloned.Deletions) 269 }) 270 271 t.Run("cloning partially inited BitmapLayer", func(t *testing.T) { 272 additions := NewBitmap(1) 273 deletions := NewBitmap(100) 274 275 layerAdditions := BitmapLayer{Additions: additions} 276 layerDeletions := BitmapLayer{Deletions: deletions} 277 278 clonedLayerAdditions := layerAdditions.Clone() 279 clonedLayerDeletions := layerDeletions.Clone() 280 additions.Remove(1) 281 deletions.Remove(100) 282 283 assert.True(t, layerAdditions.Additions.IsEmpty()) 284 assert.ElementsMatch(t, []uint64{1}, clonedLayerAdditions.Additions.ToArray()) 285 assert.Nil(t, clonedLayerAdditions.Deletions) 286 287 assert.True(t, layerDeletions.Deletions.IsEmpty()) 288 assert.Nil(t, clonedLayerDeletions.Additions) 289 assert.ElementsMatch(t, []uint64{100}, clonedLayerDeletions.Deletions.ToArray()) 290 }) 291 292 t.Run("cloning fully inited BitmapLayer", func(t *testing.T) { 293 additions := NewBitmap(1) 294 deletions := NewBitmap(100) 295 296 layer := BitmapLayer{Additions: additions, Deletions: deletions} 297 298 clonedLayer := layer.Clone() 299 additions.Remove(1) 300 deletions.Remove(100) 301 302 assert.True(t, layer.Additions.IsEmpty()) 303 assert.True(t, layer.Deletions.IsEmpty()) 304 assert.ElementsMatch(t, []uint64{1}, clonedLayer.Additions.ToArray()) 305 assert.ElementsMatch(t, []uint64{100}, clonedLayer.Deletions.ToArray()) 306 }) 307 } 308 309 // This test aims to prevent a regression on 310 // https://github.com/weaviate/sroar/issues/1 311 // found in Serialized Roaring Bitmaps library 312 func Test_BitmapLayers_Merge_PanicSliceBoundOutOfRange(t *testing.T) { 313 genSlice := func(fromInc, toExc uint64) []uint64 { 314 slice := []uint64{} 315 for i := fromInc; i < toExc; i++ { 316 slice = append(slice, i) 317 } 318 return slice 319 } 320 321 leftLayer := BitmapLayer{Deletions: NewBitmap(genSlice(289_800, 290_100)...)} 322 rightLayer := BitmapLayer{Additions: NewBitmap(genSlice(290_000, 293_000)...)} 323 324 failingDeletionsLayer, err := BitmapLayers{leftLayer, rightLayer}.Merge() 325 assert.Nil(t, err) 326 327 assert.ElementsMatch(t, genSlice(289_800, 290_000), failingDeletionsLayer.Deletions.ToArray()) 328 }