github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/persist/fs/msgpack/encoder_decoder_prop_test.go (about)

     1  // +build big
     2  //
     3  // Copyright (c) 2018 Uber Technologies, Inc.
     4  //
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  //
    12  // The above copyright notice and this permission notice shall be included in
    13  // all copies or substantial portions of the Software.
    14  //
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    21  // THE SOFTWARE.
    22  
    23  package msgpack
    24  
    25  import (
    26  	"fmt"
    27  	"math"
    28  	"math/rand"
    29  	"os"
    30  	"reflect"
    31  	"testing"
    32  	"time"
    33  
    34  	"github.com/m3db/m3/src/dbnode/persist/schema"
    35  	"github.com/m3db/m3/src/x/errors"
    36  
    37  	"github.com/leanovate/gopter"
    38  	"github.com/leanovate/gopter/gen"
    39  	"github.com/leanovate/gopter/prop"
    40  )
    41  
    42  const minSuccessfulTests = 100000
    43  
    44  func TestCommitlogFastEncodeDecodeLogEntryPropTest(t *testing.T) {
    45  	var (
    46  		parameters = gopter.DefaultTestParameters()
    47  		seed       = time.Now().UnixNano()
    48  		props      = gopter.NewProperties(parameters)
    49  		reporter   = gopter.NewFormatedReporter(true, 160, os.Stdout)
    50  	)
    51  
    52  	parameters.MinSuccessfulTests = minSuccessfulTests
    53  	parameters.Rng.Seed(seed)
    54  
    55  	props.Property("Encodes and decodes successfully", prop.ForAll(func(input schema.LogEntry) (bool, error) {
    56  		buf := []byte{}
    57  		encoded, err := EncodeLogEntryFast(buf, input)
    58  		if err != nil {
    59  			return false, errors.Wrap(err, "error encoding log entry")
    60  		}
    61  		decoded, err := DecodeLogEntryFast(encoded)
    62  		if err != nil {
    63  			return false, errors.Wrap(err, "error decoding log entry")
    64  		}
    65  
    66  		if !reflect.DeepEqual(input, decoded) {
    67  			return false, fmt.Errorf("expected: %v, but got: %v", input, decoded)
    68  		}
    69  
    70  		return true, nil
    71  	}, genLogEntry()))
    72  
    73  	if !props.Run(reporter) {
    74  		t.Errorf("failed with initial seed: %d", seed)
    75  	}
    76  }
    77  
    78  func TestCommitlogFastEncodeDecodeLogMetadataPropTest(t *testing.T) {
    79  	var (
    80  		parameters = gopter.DefaultTestParameters()
    81  		seed       = time.Now().UnixNano()
    82  		props      = gopter.NewProperties(parameters)
    83  		reporter   = gopter.NewFormatedReporter(true, 160, os.Stdout)
    84  	)
    85  
    86  	parameters.MinSuccessfulTests = minSuccessfulTests
    87  	parameters.Rng.Seed(seed)
    88  
    89  	props.Property("Encodes and decodes successfully", prop.ForAll(func(input schema.LogMetadata) (bool, error) {
    90  		buf := []byte{}
    91  		encoded, err := EncodeLogMetadataFast(buf, input)
    92  		if err != nil {
    93  			return false, errors.Wrap(err, "error encoding log entry")
    94  		}
    95  		decoded, err := DecodeLogMetadataFast(encoded)
    96  		if err != nil {
    97  			return false, errors.Wrap(err, "error decoding log entry")
    98  		}
    99  
   100  		if !reflect.DeepEqual(input, decoded) {
   101  			return false, fmt.Errorf("expected: %v, but got: %v", input, decoded)
   102  		}
   103  
   104  		return true, nil
   105  	}, genLogMetadata()))
   106  
   107  	if !props.Run(reporter) {
   108  		t.Errorf("failed with initial seed: %d", seed)
   109  	}
   110  }
   111  
   112  func TestCommitlogFastDecodeCorruptLogEntryPropTest(t *testing.T) {
   113  	var (
   114  		parameters = gopter.DefaultTestParameters()
   115  		seed       = time.Now().UnixNano()
   116  		props      = gopter.NewProperties(parameters)
   117  		reporter   = gopter.NewFormatedReporter(true, 160, os.Stdout)
   118  		rng        = rand.New(rand.NewSource(seed))
   119  	)
   120  
   121  	parameters.MinSuccessfulTests = minSuccessfulTests
   122  	parameters.Rng.Seed(seed)
   123  
   124  	props.Property("Encodes and decodes successfully with arbitrary corruption", prop.ForAll(func(input schema.LogEntry) (bool, error) {
   125  		// Generate a valid byte stream.
   126  		buf := []byte{}
   127  		encoded, err := EncodeLogEntryFast(buf, input)
   128  		if err != nil {
   129  			return false, errors.Wrap(err, "error encoding log entry")
   130  		}
   131  
   132  		// Corrupt a single byte randomly and make sure it doesn't panic
   133  		// when we try and decode.
   134  		corruptIdx := rng.Intn(len(encoded))
   135  		corruptVal := uint8(rng.Intn(math.MaxUint8))
   136  		encoded[corruptIdx] = corruptVal
   137  
   138  		// Ignore result and errors. We're just checking for panics. Errors are meaningless
   139  		// because sometimes the corruption may generate an invalid msgpack encoding, and sometimes
   140  		// it may still be decodable.
   141  		DecodeLogEntryFast(encoded)
   142  
   143  		return true, nil
   144  	}, genLogEntry()))
   145  
   146  	props.Property("Encodes and decodes successfully with arbitrary truncation", prop.ForAll(func(input schema.LogEntry) (bool, error) {
   147  		// Generate a valid byte stream.
   148  		buf := []byte{}
   149  		encoded, err := EncodeLogEntryFast(buf, input)
   150  		if err != nil {
   151  			return false, errors.Wrap(err, "error encoding log entry")
   152  		}
   153  
   154  		// Pick an arbitrary spot to truncate the stream.
   155  		corruptIdx := rng.Intn(len(encoded))
   156  		encoded = encoded[:corruptIdx]
   157  
   158  		// Ignore result and errors. We're just checking for panics. Errors are meaningless
   159  		// because sometimes the corruption may generate an invalid msgpack encoding, and sometimes
   160  		// it may still be decodable.
   161  		DecodeLogEntryFast(encoded)
   162  
   163  		return true, nil
   164  	}, genLogEntry()))
   165  
   166  	if !props.Run(reporter) {
   167  		t.Errorf("failed with initial seed: %d", seed)
   168  	}
   169  }
   170  
   171  func genLogEntry() gopter.Gen {
   172  	return gopter.CombineGens(
   173  		gen.UInt64(),
   174  		gen.Int64(),
   175  		genByteSlice(),
   176  		gen.Int64(),
   177  		gen.Float64(),
   178  		gen.UInt32(),
   179  		genByteSlice(),
   180  	).Map(func(inputs []interface{}) schema.LogEntry {
   181  		return schema.LogEntry{
   182  			Index:      inputs[0].(uint64),
   183  			Create:     inputs[1].(int64),
   184  			Metadata:   inputs[2].([]byte),
   185  			Timestamp:  inputs[3].(int64),
   186  			Value:      inputs[4].(float64),
   187  			Unit:       inputs[5].(uint32),
   188  			Annotation: inputs[6].([]byte),
   189  		}
   190  	})
   191  }
   192  
   193  func genLogMetadata() gopter.Gen {
   194  	return gopter.CombineGens(
   195  		genByteSlice(),
   196  		genByteSlice(),
   197  		gen.UInt32(),
   198  		genByteSlice(),
   199  	).Map(func(inputs []interface{}) schema.LogMetadata {
   200  		return schema.LogMetadata{
   201  			ID:          inputs[0].([]byte),
   202  			Namespace:   inputs[1].([]byte),
   203  			Shard:       inputs[2].(uint32),
   204  			EncodedTags: inputs[3].([]byte),
   205  		}
   206  	})
   207  }
   208  
   209  func genByteSlice() gopter.Gen {
   210  	return gen.SliceOf(gen.UInt8())
   211  }