github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/cmd/roachtest/test_test.go (about)

     1  // Copyright 2018 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 main
    12  
    13  import (
    14  	"bytes"
    15  	"context"
    16  	"io/ioutil"
    17  	"regexp"
    18  	"sort"
    19  	"strings"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/cockroachdb/cockroach/pkg/testutils"
    24  	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
    25  	"github.com/cockroachdb/cockroach/pkg/util/version"
    26  	"github.com/kr/pretty"
    27  )
    28  
    29  const OwnerUnitTest Owner = `unittest`
    30  
    31  const defaultParallelism = 10
    32  
    33  func TestMatchOrSkip(t *testing.T) {
    34  	testCases := []struct {
    35  		filter       []string
    36  		name         string
    37  		tags         []string
    38  		expected     bool
    39  		expectedSkip string
    40  	}{
    41  		{nil, "foo", nil, true, ""},
    42  		{nil, "foo", []string{"bar"}, true, "[tag:default] does not match [bar]"},
    43  		{[]string{"tag:b"}, "foo", []string{"bar"}, true, ""},
    44  		{[]string{"tag:b"}, "foo", nil, true, "[tag:b] does not match [default]"},
    45  		{[]string{"tag:default"}, "foo", nil, true, ""},
    46  		{[]string{"tag:f"}, "foo", []string{"bar"}, true, "[tag:f] does not match [bar]"},
    47  		{[]string{"f"}, "foo", []string{"bar"}, true, "[tag:default] does not match [bar]"},
    48  		{[]string{"f"}, "bar", []string{"bar"}, false, ""},
    49  		{[]string{"f", "tag:b"}, "foo", []string{"bar"}, true, ""},
    50  		{[]string{"f", "tag:f"}, "foo", []string{"bar"}, true, "[tag:f] does not match [bar]"},
    51  	}
    52  	for _, c := range testCases {
    53  		t.Run("", func(t *testing.T) {
    54  			f := newFilter(c.filter)
    55  			spec := &testSpec{Name: c.name, Owner: OwnerUnitTest, Tags: c.tags}
    56  			if value := spec.matchOrSkip(f); c.expected != value {
    57  				t.Fatalf("expected %t, but found %t", c.expected, value)
    58  			} else if value && c.expectedSkip != spec.Skip {
    59  				t.Fatalf("expected %s, but found %s", c.expectedSkip, spec.Skip)
    60  			}
    61  		})
    62  	}
    63  }
    64  
    65  func nilLogger() *logger {
    66  	lcfg := loggerConfig{
    67  		stdout: ioutil.Discard,
    68  		stderr: ioutil.Discard,
    69  	}
    70  	l, err := lcfg.newLogger("" /* path */)
    71  	if err != nil {
    72  		panic(err)
    73  	}
    74  	return l
    75  }
    76  
    77  func TestRunnerRun(t *testing.T) {
    78  	ctx := context.Background()
    79  	r, err := makeTestRegistry()
    80  	if err != nil {
    81  		t.Fatal(err)
    82  	}
    83  	r.Add(testSpec{
    84  		Name:    "pass",
    85  		Owner:   OwnerUnitTest,
    86  		Run:     func(ctx context.Context, t *test, c *cluster) {},
    87  		Cluster: makeClusterSpec(0),
    88  	})
    89  	r.Add(testSpec{
    90  		Name:  "fail",
    91  		Owner: OwnerUnitTest,
    92  		Run: func(ctx context.Context, t *test, c *cluster) {
    93  			t.Fatal("failed")
    94  		},
    95  		Cluster: makeClusterSpec(0),
    96  	})
    97  
    98  	testCases := []struct {
    99  		filters []string
   100  		expErr  string
   101  	}{
   102  		{nil, "some tests failed"},
   103  		{[]string{"pass"}, ""},
   104  		{[]string{"fail"}, "some tests failed"},
   105  		{[]string{"pass|fail"}, "some tests failed"},
   106  		{[]string{"pass", "fail"}, "some tests failed"},
   107  		{[]string{"notests"}, "no test"},
   108  	}
   109  	for _, c := range testCases {
   110  		t.Run("", func(t *testing.T) {
   111  			tests := testsToRun(ctx, r, newFilter(c.filters))
   112  			cr := newClusterRegistry()
   113  			runner := newTestRunner(cr, r.buildVersion)
   114  
   115  			lopt := loggingOpt{
   116  				l:            nilLogger(),
   117  				tee:          noTee,
   118  				stdout:       ioutil.Discard,
   119  				stderr:       ioutil.Discard,
   120  				artifactsDir: "",
   121  			}
   122  			copt := clustersOpt{
   123  				typ:                       roachprodCluster,
   124  				user:                      "test_user",
   125  				cpuQuota:                  1000,
   126  				keepClustersOnTestFailure: false,
   127  			}
   128  			err := runner.Run(ctx, tests, 1, /* count */
   129  				defaultParallelism, copt, "" /* artifactsDir */, lopt)
   130  
   131  			if !testutils.IsError(err, c.expErr) {
   132  				t.Fatalf("expected err: %q, but found %v. Filters: %s", c.expErr, err, c.filters)
   133  			}
   134  		})
   135  	}
   136  }
   137  
   138  type syncedBuffer struct {
   139  	mu  syncutil.Mutex
   140  	buf bytes.Buffer
   141  }
   142  
   143  func (b *syncedBuffer) Write(p []byte) (n int, err error) {
   144  	b.mu.Lock()
   145  	defer b.mu.Unlock()
   146  	return b.buf.Write(p)
   147  }
   148  
   149  func (b *syncedBuffer) String() string {
   150  	b.mu.Lock()
   151  	defer b.mu.Unlock()
   152  	return b.buf.String()
   153  }
   154  
   155  func TestRunnerTestTimeout(t *testing.T) {
   156  	ctx := context.Background()
   157  
   158  	cr := newClusterRegistry()
   159  	runner := newTestRunner(cr, version.Version{})
   160  
   161  	var buf syncedBuffer
   162  	lopt := loggingOpt{
   163  		l:            nilLogger(),
   164  		tee:          noTee,
   165  		stdout:       &buf,
   166  		stderr:       &buf,
   167  		artifactsDir: "",
   168  	}
   169  	copt := clustersOpt{
   170  		typ:                       roachprodCluster,
   171  		user:                      "test_user",
   172  		cpuQuota:                  1000,
   173  		keepClustersOnTestFailure: false,
   174  	}
   175  	test := testSpec{
   176  		Name:    `timeout`,
   177  		Owner:   OwnerUnitTest,
   178  		Timeout: 10 * time.Millisecond,
   179  		Cluster: makeClusterSpec(0),
   180  		Run: func(ctx context.Context, t *test, c *cluster) {
   181  			<-ctx.Done()
   182  		},
   183  	}
   184  	err := runner.Run(ctx, []testSpec{test}, 1, /* count */
   185  		defaultParallelism, copt, "" /* artifactsDir */, lopt)
   186  	if !testutils.IsError(err, "some tests failed") {
   187  		t.Fatalf("expected error \"some tests failed\", got: %v", err)
   188  	}
   189  
   190  	out := buf.String()
   191  	timeoutRE := regexp.MustCompile(`(?m)^.*test timed out \(.*\)$`)
   192  	if !timeoutRE.MatchString(out) {
   193  		t.Fatalf("unable to find \"timed out\" message:\n%s", out)
   194  	}
   195  }
   196  
   197  func TestRegistryPrepareSpec(t *testing.T) {
   198  	dummyRun := func(context.Context, *test, *cluster) {}
   199  
   200  	var listTests = func(t *testSpec) []string {
   201  		return []string{t.Name}
   202  	}
   203  
   204  	testCases := []struct {
   205  		spec          testSpec
   206  		expectedErr   string
   207  		expectedTests []string
   208  	}{
   209  		{
   210  			testSpec{
   211  				Name:    "a",
   212  				Owner:   OwnerUnitTest,
   213  				Run:     dummyRun,
   214  				Cluster: makeClusterSpec(0),
   215  			},
   216  			"",
   217  			[]string{"a"},
   218  		},
   219  		{
   220  			testSpec{
   221  				Name:       "a",
   222  				Owner:      OwnerUnitTest,
   223  				MinVersion: "v2.1.0",
   224  				Run:        dummyRun,
   225  				Cluster:    makeClusterSpec(0),
   226  			},
   227  			"",
   228  			[]string{"a"},
   229  		},
   230  		{
   231  			testSpec{
   232  				Name:       "a",
   233  				Owner:      OwnerUnitTest,
   234  				MinVersion: "foo",
   235  				Run:        dummyRun,
   236  				Cluster:    makeClusterSpec(0),
   237  			},
   238  			"a: unable to parse min-version: invalid version string 'foo'",
   239  			nil,
   240  		},
   241  	}
   242  	for _, c := range testCases {
   243  		t.Run("", func(t *testing.T) {
   244  			r, err := makeTestRegistry()
   245  			if err != nil {
   246  				t.Fatal(err)
   247  			}
   248  			err = r.prepareSpec(&c.spec)
   249  			if !testutils.IsError(err, c.expectedErr) {
   250  				t.Fatalf("expected %q, but found %q", c.expectedErr, err.Error())
   251  			}
   252  			if c.expectedErr == "" {
   253  				tests := listTests(&c.spec)
   254  				sort.Strings(tests)
   255  				if diff := pretty.Diff(c.expectedTests, tests); len(diff) != 0 {
   256  					t.Fatalf("unexpected tests:\n%s", strings.Join(diff, "\n"))
   257  				}
   258  			}
   259  		})
   260  	}
   261  }
   262  
   263  func TestRegistryMinVersion(t *testing.T) {
   264  	ctx := context.Background()
   265  	testCases := []struct {
   266  		buildVersion string
   267  		expectedA    bool
   268  		expectedB    bool
   269  		expErr       string
   270  	}{
   271  		{"v1.1.0", false, false, "no test matched filters"},
   272  		{"v2.0.0", true, false, ""},
   273  		{"v2.1.0", true, true, ""},
   274  	}
   275  	for _, c := range testCases {
   276  		t.Run(c.buildVersion, func(t *testing.T) {
   277  			var runA, runB bool
   278  			r, err := makeTestRegistry()
   279  			if err != nil {
   280  				t.Fatal(err)
   281  			}
   282  			r.Add(testSpec{
   283  				Name:       "a",
   284  				Owner:      OwnerUnitTest,
   285  				MinVersion: "v2.0.0",
   286  				Cluster:    makeClusterSpec(0),
   287  				Run: func(ctx context.Context, t *test, c *cluster) {
   288  					runA = true
   289  				},
   290  			})
   291  			r.Add(testSpec{
   292  				Name:       "b",
   293  				Owner:      OwnerUnitTest,
   294  				MinVersion: "v2.1.0",
   295  				Cluster:    makeClusterSpec(0),
   296  				Run: func(ctx context.Context, t *test, c *cluster) {
   297  					runB = true
   298  				},
   299  			})
   300  			if err := r.setBuildVersion(c.buildVersion); err != nil {
   301  				t.Fatal(err)
   302  			}
   303  			tests := testsToRun(ctx, r, newFilter(nil))
   304  
   305  			var buf syncedBuffer
   306  			lopt := loggingOpt{
   307  				l:            nilLogger(),
   308  				tee:          noTee,
   309  				stdout:       &buf,
   310  				stderr:       &buf,
   311  				artifactsDir: "",
   312  			}
   313  			copt := clustersOpt{
   314  				typ:                       roachprodCluster,
   315  				user:                      "test_user",
   316  				cpuQuota:                  1000,
   317  				keepClustersOnTestFailure: false,
   318  			}
   319  			cr := newClusterRegistry()
   320  			runner := newTestRunner(cr, r.buildVersion)
   321  			err = runner.Run(ctx, tests, 1, /* count */
   322  				defaultParallelism, copt, "" /* artifactsDir */, lopt)
   323  			if !testutils.IsError(err, c.expErr) {
   324  				t.Fatalf("expected err: %q, got: %v", c.expErr, err)
   325  			}
   326  
   327  			if c.expectedA != runA || c.expectedB != runB {
   328  				t.Fatalf("expected %t,%t, but got %t,%t\n%s",
   329  					c.expectedA, c.expectedB, runA, runB, buf.String())
   330  			}
   331  		})
   332  	}
   333  }