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  }