github.com/m3db/m3@v1.5.0/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 }