github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/m3ninx/search/proptest/segment_gen.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package proptest
    22  
    23  import (
    24  	"fmt"
    25  	"math/rand"
    26  	"testing"
    27  
    28  	"github.com/m3db/m3/src/m3ninx/doc"
    29  	"github.com/m3db/m3/src/m3ninx/index/segment"
    30  	"github.com/m3db/m3/src/m3ninx/index/segment/fst"
    31  	"github.com/m3db/m3/src/m3ninx/index/segment/mem"
    32  
    33  	"github.com/leanovate/gopter"
    34  	"github.com/leanovate/gopter/gen"
    35  	"github.com/stretchr/testify/require"
    36  )
    37  
    38  var (
    39  	memOptions = mem.NewOptions()
    40  	fstOptions = fst.NewOptions()
    41  )
    42  
    43  func collectDocs(iter doc.Iterator) ([]doc.Document, error) {
    44  	var docs []doc.Document
    45  	for iter.Next() {
    46  		docs = append(docs, iter.Current())
    47  	}
    48  
    49  	if err := iter.Err(); err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	return docs, nil
    54  }
    55  
    56  func newTestMemSegment(t *testing.T, docs []doc.Metadata) segment.MutableSegment {
    57  	opts := mem.NewOptions()
    58  	s, err := mem.NewSegment(opts)
    59  	require.NoError(t, err)
    60  	for _, d := range docs {
    61  		_, err := s.Insert(d)
    62  		require.NoError(t, err)
    63  	}
    64  	return s
    65  }
    66  
    67  func (i propTestInput) generate(t *testing.T, docs []doc.Metadata) []segment.Segment {
    68  	var result []segment.Segment
    69  	for j := 0; j < len(i.segments); j++ {
    70  		s, err := mem.NewSegment(memOptions)
    71  		require.NoError(t, err)
    72  		for k := 0; k < len(i.docIds[j]); k++ {
    73  			idx := i.docIds[j][k]
    74  			_, err = s.Insert(docs[idx])
    75  			require.NoError(t, err)
    76  		}
    77  
    78  		if i.segments[j].simpleSegment {
    79  			result = append(result, s)
    80  			continue
    81  		}
    82  
    83  		result = append(result, fst.ToTestSegment(t, s, fstOptions))
    84  	}
    85  	return result
    86  }
    87  
    88  type propTestInput struct {
    89  	numDocs  int
    90  	segments []generatedSegment
    91  	docIds   [][]int
    92  }
    93  
    94  func genPropTestInput(numDocs int) gopter.Gen {
    95  	return func(genParams *gopter.GenParameters) *gopter.GenResult {
    96  		maxNumSegments := numDocs
    97  		if maxNumSegments > 10 {
    98  			maxNumSegments = 10
    99  		}
   100  
   101  		numSegmentsRes, ok := gen.IntRange(1, maxNumSegments)(genParams).Retrieve()
   102  		if !ok {
   103  			panic("unable to generate segments")
   104  		}
   105  		numSegments := numSegmentsRes.(int)
   106  
   107  		docIds := make([]int, 0, numDocs)
   108  		for i := 0; i < numDocs; i++ {
   109  			docIds = append(docIds, i)
   110  		}
   111  
   112  		randomIds := randomDocIds(docIds)
   113  		randomIds.shuffle(genParams.Rng)
   114  
   115  		genSegments := make([]generatedSegment, 0, numSegments)
   116  		partitionedDocs := make([][]int, 0, numSegments)
   117  		for i := 0; i < numSegments; i++ {
   118  			partitionedDocs = append(partitionedDocs, []int{})
   119  			segRes, ok := genSegment()(genParams).Retrieve()
   120  			if !ok {
   121  				panic("unable to generate segments")
   122  			}
   123  			genSegments = append(genSegments, segRes.(generatedSegment))
   124  		}
   125  
   126  		for i := 0; i < numDocs; i++ {
   127  			idx := i % numSegments
   128  			partitionedDocs[idx] = append(partitionedDocs[idx], randomIds[i])
   129  		}
   130  
   131  		result := propTestInput{
   132  			numDocs:  numDocs,
   133  			segments: genSegments,
   134  			docIds:   partitionedDocs,
   135  		}
   136  		if len(genSegments) != len(partitionedDocs) {
   137  			panic(fmt.Errorf("unequal lengths of segments and docs: %+v", result))
   138  		}
   139  
   140  		return gopter.NewGenResult(result, gopter.NoShrinker)
   141  	}
   142  }
   143  
   144  func genSegment() gopter.Gen {
   145  	return gopter.CombineGens(
   146  		gen.Bool(), // simple segment
   147  	).Map(func(val interface{}) generatedSegment {
   148  		var inputs []interface{}
   149  		if x, ok := val.(*gopter.GenResult); ok {
   150  			res, rOk := x.Retrieve()
   151  			if !rOk {
   152  				panic("should never happen")
   153  			}
   154  			inputs = res.([]interface{})
   155  		} else {
   156  			inputs = val.([]interface{})
   157  		}
   158  		return generatedSegment{
   159  			simpleSegment: inputs[0].(bool),
   160  		}
   161  	})
   162  }
   163  
   164  type generatedSegment struct {
   165  	simpleSegment bool
   166  }
   167  
   168  type randomDocIds []int
   169  
   170  func (d randomDocIds) shuffle(rng *rand.Rand) {
   171  	// Start from the last element and swap one by one.
   172  	// NB: We don't need to run for the first element that's why i > 0
   173  	for i := len(d) - 1; i > 0; i-- {
   174  		j := rng.Intn(i)
   175  		d[i], d[j] = d[j], d[i]
   176  	}
   177  }