github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/test/builder.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 test
    22  
    23  import (
    24  	"fmt"
    25  	"time"
    26  
    27  	"github.com/m3db/m3/src/query/block"
    28  	"github.com/m3db/m3/src/query/models"
    29  	"github.com/m3db/m3/src/query/storage"
    30  	"github.com/m3db/m3/src/query/ts"
    31  	xtime "github.com/m3db/m3/src/x/time"
    32  
    33  	"github.com/prometheus/common/model"
    34  )
    35  
    36  // ValueMod can be used to modify provided values for testing.
    37  type ValueMod func([]float64) []float64
    38  
    39  // NoopMod can be used to generate multi blocks when no value
    40  // modification is needed.
    41  func NoopMod(v []float64) []float64 {
    42  	return v
    43  }
    44  
    45  // NewBlockFromValues creates a new block using the provided values.
    46  func NewBlockFromValues(
    47  	bounds models.Bounds,
    48  	seriesValues [][]float64,
    49  ) block.Block {
    50  	meta := NewSeriesMeta("dummy", len(seriesValues))
    51  	return NewBlockFromValuesWithSeriesMeta(bounds, meta, seriesValues)
    52  }
    53  
    54  // NewUnconsolidatedBlockFromDatapointsWithMeta creates a new unconsolidated
    55  // block using the provided values and metadata.
    56  func NewUnconsolidatedBlockFromDatapointsWithMeta(
    57  	bounds models.Bounds,
    58  	seriesMetas []block.SeriesMeta,
    59  	resultMeta block.ResultMetadata,
    60  	seriesValues [][]float64,
    61  	enableBatched bool,
    62  ) block.Block {
    63  	seriesList := make(ts.SeriesList, len(seriesValues))
    64  	for i, values := range seriesValues {
    65  		dps := seriesValuesToDatapoints(values, bounds)
    66  		seriesList[i] = ts.NewSeries(seriesMetas[i].Name, dps, seriesMetas[i].Tags)
    67  	}
    68  
    69  	result := &storage.FetchResult{
    70  		SeriesList: seriesList,
    71  		Metadata:   resultMeta,
    72  	}
    73  
    74  	return newMultiSeriesBlock(result, &storage.FetchQuery{
    75  		Start:    bounds.Start.ToTime(),
    76  		End:      bounds.End().ToTime(),
    77  		Interval: bounds.StepSize,
    78  	}, time.Minute, enableBatched)
    79  }
    80  
    81  func seriesValuesToDatapoints(
    82  	values []float64,
    83  	bounds models.Bounds,
    84  ) ts.Datapoints {
    85  	dps := make(ts.Datapoints, len(values))
    86  	for i, v := range values {
    87  		t, _ := bounds.TimeForIndex(i)
    88  		dps[i] = ts.Datapoint{
    89  			Timestamp: t.Add(-1 * time.Microsecond),
    90  			Value:     v,
    91  		}
    92  	}
    93  
    94  	return dps
    95  }
    96  
    97  // NewSeriesMetaWithoutName creates new metadata tags in the format
    98  // [tagPrefix:i] for the number of series, without including the __name__.
    99  func NewSeriesMetaWithoutName(tagPrefix string, count int) []block.SeriesMeta {
   100  	return newSeriesMeta(tagPrefix, count, false)
   101  }
   102  
   103  // NewSeriesMeta creates new metadata tags in the format [tagPrefix:i]
   104  // for the number of series.
   105  func NewSeriesMeta(tagPrefix string, count int) []block.SeriesMeta {
   106  	return newSeriesMeta(tagPrefix, count, true)
   107  }
   108  
   109  func newSeriesMeta(tagPrefix string, count int, name bool) []block.SeriesMeta {
   110  	seriesMeta := make([]block.SeriesMeta, count)
   111  	for i := range seriesMeta {
   112  		tags := models.EmptyTags()
   113  		st := []byte(fmt.Sprintf("%s%d", tagPrefix, i))
   114  		if name {
   115  			tags = tags.AddTag(models.Tag{
   116  				Name:  []byte(model.MetricNameLabel),
   117  				Value: st,
   118  			})
   119  		}
   120  
   121  		tags = tags.AddTag(models.Tag{Name: st, Value: st})
   122  		seriesMeta[i] = block.SeriesMeta{
   123  			Name: st,
   124  			Tags: tags,
   125  		}
   126  	}
   127  
   128  	return seriesMeta
   129  }
   130  
   131  // NewBlockFromValuesWithSeriesMeta creates a new block using the
   132  // provided values.
   133  func NewBlockFromValuesWithSeriesMeta(
   134  	bounds models.Bounds,
   135  	seriesMeta []block.SeriesMeta,
   136  	seriesValues [][]float64,
   137  ) block.Block {
   138  	blockMeta := block.Metadata{
   139  		Bounds:         bounds,
   140  		Tags:           models.NewTags(0, models.NewTagOptions()),
   141  		ResultMetadata: block.NewResultMetadata(),
   142  	}
   143  
   144  	return NewBlockFromValuesWithMetaAndSeriesMeta(
   145  		blockMeta,
   146  		seriesMeta,
   147  		seriesValues,
   148  	)
   149  }
   150  
   151  // NewBlockFromValuesWithMetaAndSeriesMeta creates a new block using the
   152  // provided values.
   153  func NewBlockFromValuesWithMetaAndSeriesMeta(
   154  	meta block.Metadata,
   155  	seriesMeta []block.SeriesMeta,
   156  	seriesValues [][]float64,
   157  ) block.Block {
   158  	columnBuilder := block.NewColumnBlockBuilder(
   159  		models.NoopQueryContext(),
   160  		meta,
   161  		seriesMeta,
   162  	)
   163  
   164  	if err := columnBuilder.AddCols(meta.Bounds.Steps()); err != nil {
   165  		panic(err)
   166  	}
   167  
   168  	if len(seriesValues) > 0 {
   169  		for _, seriesVal := range seriesValues {
   170  			if meta.Bounds.Steps() != len(seriesVal) {
   171  				panic("invalid bounds for test series")
   172  			}
   173  
   174  			for idx, val := range seriesVal {
   175  				if err := columnBuilder.AppendValue(idx, val); err != nil {
   176  					panic(err)
   177  				}
   178  			}
   179  		}
   180  	}
   181  
   182  	return columnBuilder.Build()
   183  }
   184  
   185  // GenerateValuesAndBounds generates a list of sample values and bounds while
   186  // allowing overrides.
   187  func GenerateValuesAndBounds(
   188  	vals [][]float64,
   189  	b *models.Bounds,
   190  ) ([][]float64, models.Bounds) {
   191  	values := vals
   192  	if values == nil {
   193  		values = [][]float64{
   194  			{0, 1, 2, 3, 4},
   195  			{5, 6, 7, 8, 9},
   196  		}
   197  	}
   198  
   199  	var bounds models.Bounds
   200  	if b == nil {
   201  		now := xtime.Now()
   202  		bounds = models.Bounds{
   203  			Start:    now,
   204  			Duration: time.Minute * 5,
   205  			StepSize: time.Minute,
   206  		}
   207  	} else {
   208  		bounds = *b
   209  	}
   210  
   211  	return values, bounds
   212  }