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  }