github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/storage/metamorphic/meta_test.go (about) 1 // Copyright 2020 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package metamorphic 12 13 import ( 14 "context" 15 "flag" 16 "fmt" 17 "io" 18 "math/rand" 19 "os" 20 "path/filepath" 21 "strings" 22 "testing" 23 24 "github.com/cockroachdb/cockroach/pkg/testutils" 25 "github.com/cockroachdb/cockroach/pkg/util" 26 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 27 ) 28 29 var ( 30 keep = flag.Bool("keep", false, "keep temp directories after test") 31 check = flag.String("check", "", "run operations in specified file and check output for equality") 32 seed = flag.Int64("seed", 456, "specify seed to use for random number generator") 33 opCount = flag.Int("operations", 1000, "number of MVCC operations to generate and run") 34 ) 35 36 type testRun struct { 37 ctx context.Context 38 t *testing.T 39 seed int64 40 checkFile string 41 restarts bool 42 engineSequences [][]engineImpl 43 } 44 45 type testRunForEngines struct { 46 ctx context.Context 47 t *testing.T 48 seed int64 49 restarts bool 50 checkFile io.Reader 51 outputFile io.Writer 52 engineSequence []engineImpl 53 } 54 55 func runMetaTestForEngines(run testRunForEngines) { 56 tempDir, cleanup := testutils.TempDir(run.t) 57 defer func() { 58 if !*keep { 59 cleanup() 60 } 61 }() 62 63 testRunner := metaTestRunner{ 64 ctx: run.ctx, 65 t: run.t, 66 w: run.outputFile, 67 seed: run.seed, 68 restarts: run.restarts, 69 engineImpls: run.engineSequence, 70 path: filepath.Join(tempDir, "store"), 71 } 72 fmt.Printf("store path = %s\n", testRunner.path) 73 74 testRunner.init() 75 defer testRunner.closeAll() 76 if run.checkFile != nil { 77 testRunner.parseFileAndRun(run.checkFile) 78 } else { 79 testRunner.generateAndRun(*opCount) 80 } 81 } 82 83 func runMetaTest(run testRun) { 84 t := run.t 85 outerTempDir, cleanup := testutils.TempDir(run.t) 86 defer func() { 87 if !*keep { 88 cleanup() 89 } 90 }() 91 92 // The test run with the first engine sequence writes its output to this file. 93 // All subsequent engine sequence runs compare their output against this file. 94 firstRunOutput := filepath.Join(outerTempDir, "output.meta") 95 firstRunExecuted := false 96 fmt.Printf("first run output file: %s\n", firstRunOutput) 97 98 for _, engineSequence := range run.engineSequences { 99 var engineNames []string 100 for _, engineImpl := range engineSequence { 101 engineNames = append(engineNames, engineImpl.name) 102 } 103 104 t.Run(strings.Join(engineNames, ","), func(t *testing.T) { 105 innerTempDir, cleanup := testutils.TempDir(t) 106 defer func() { 107 if !*keep { 108 cleanup() 109 } 110 }() 111 112 // If this is not the first sequence run and a "check" file was not passed 113 // in, use the first run's output file as the check file. 114 var checkFileReader io.ReadCloser 115 if run.checkFile == "" && firstRunExecuted { 116 run.checkFile = firstRunOutput 117 } 118 if run.checkFile != "" { 119 var err error 120 checkFileReader, err = os.Open(run.checkFile) 121 if err != nil { 122 t.Fatal(err) 123 } 124 defer checkFileReader.Close() 125 } 126 127 var outputFileWriter io.WriteCloser 128 outputFile := firstRunOutput 129 if firstRunExecuted { 130 outputFile = filepath.Join(innerTempDir, "output.meta") 131 } 132 var err error 133 outputFileWriter, err = os.Create(outputFile) 134 if err != nil { 135 t.Fatal(err) 136 } 137 defer outputFileWriter.Close() 138 fmt.Printf("check file = %s\noutput file = %s\n", run.checkFile, outputFile) 139 engineRun := testRunForEngines{ 140 ctx: run.ctx, 141 t: t, 142 seed: run.seed, 143 restarts: run.restarts, 144 checkFile: checkFileReader, 145 outputFile: outputFileWriter, 146 engineSequence: engineSequence, 147 } 148 runMetaTestForEngines(engineRun) 149 firstRunExecuted = true 150 }) 151 } 152 } 153 154 // TestRocksPebbleEquivalence runs the MVCC Metamorphic test suite, and checks 155 // for matching outputs by the test suite between RocksDB and Pebble. 156 func TestRocksPebbleEquivalence(t *testing.T) { 157 defer leaktest.AfterTest(t) 158 ctx := context.Background() 159 if util.RaceEnabled { 160 // This test times out with the race detector enabled. 161 return 162 } 163 164 // Have one fixed seed, one user-specified seed, and one random seed. 165 seeds := []int64{123, *seed, rand.Int63()} 166 167 for _, seed := range seeds { 168 t.Run(fmt.Sprintf("seed=%d", seed), func(t *testing.T) { 169 run := testRun{ 170 ctx: ctx, 171 t: t, 172 seed: seed, 173 restarts: false, 174 engineSequences: [][]engineImpl{ 175 {engineImplRocksDB}, 176 {engineImplPebble}, 177 {engineImplPebbleManySSTs}, 178 {engineImplPebbleVarOpts}, 179 }, 180 } 181 runMetaTest(run) 182 }) 183 } 184 } 185 186 // TestRocksPebbleRestarts runs the MVCC Metamorphic test suite with restarts 187 // enabled, and ensures that the output remains the same across different 188 // engine sequences with restarts in between. 189 func TestRocksPebbleRestarts(t *testing.T) { 190 defer leaktest.AfterTest(t) 191 ctx := context.Background() 192 if util.RaceEnabled { 193 // This test times out with the race detector enabled. 194 return 195 } 196 197 // Have one fixed seed, one user-specified seed, and one random seed. 198 seeds := []int64{123, *seed, rand.Int63()} 199 200 for _, seed := range seeds { 201 t.Run(fmt.Sprintf("seed=%d", seed), func(t *testing.T) { 202 run := testRun{ 203 ctx: ctx, 204 t: t, 205 seed: seed, 206 restarts: true, 207 engineSequences: [][]engineImpl{ 208 {engineImplRocksDB}, 209 {engineImplPebble}, 210 {engineImplRocksDB, engineImplPebble}, 211 {engineImplRocksDB, engineImplPebbleManySSTs, engineImplPebbleVarOpts}, 212 }, 213 } 214 runMetaTest(run) 215 }) 216 } 217 } 218 219 // TestRocksPebbleCheck checks whether the output file specified with --check has 220 // matching behavior across rocks/pebble. 221 func TestRocksPebbleCheck(t *testing.T) { 222 defer leaktest.AfterTest(t) 223 ctx := context.Background() 224 225 if *check != "" { 226 if _, err := os.Stat(*check); os.IsNotExist(err) { 227 t.Fatal(err) 228 } 229 230 run := testRun{ 231 ctx: ctx, 232 t: t, 233 checkFile: *check, 234 restarts: true, 235 engineSequences: [][]engineImpl{ 236 {engineImplRocksDB}, 237 {engineImplPebble}, 238 {engineImplRocksDB, engineImplPebble}, 239 }, 240 } 241 runMetaTest(run) 242 } 243 }