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

     1  // Copyright 2019 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  	"bufio"
    15  	"bytes"
    16  	"context"
    17  	"fmt"
    18  	"regexp"
    19  	"strings"
    20  )
    21  
    22  var sqlAlchemyResultRegex = regexp.MustCompile(`^(?P<test>test.*::.*::[^ \[\]]*(?:\[.*])?) (?P<result>.*)$`)
    23  var sqlAlchemyReleaseTagRegex = regexp.MustCompile(`^rel_(?P<major>\d+)_(?P<minor>\d+)_(?P<point>\d+)$`)
    24  
    25  // This test runs the SQLAlchemy dialect test suite against a single Cockroach
    26  // node.
    27  
    28  func registerSQLAlchemy(r *testRegistry) {
    29  	runSQLAlchemy := func(
    30  		ctx context.Context,
    31  		t *test,
    32  		c *cluster,
    33  	) {
    34  		if c.isLocal() {
    35  			t.Fatal("cannot be run in local mode")
    36  		}
    37  		node := c.Node(1)
    38  		t.Status("setting up cockroach")
    39  		c.Put(ctx, cockroach, "./cockroach", c.All())
    40  		c.Start(ctx, t, c.All())
    41  
    42  		version, err := fetchCockroachVersion(ctx, c, node[0])
    43  		if err != nil {
    44  			t.Fatal(err)
    45  		}
    46  
    47  		if err := alterZoneConfigAndClusterSettings(ctx, version, c, node[0]); err != nil {
    48  			t.Fatal(err)
    49  		}
    50  
    51  		t.Status("cloning sqlalchemy and installing prerequisites")
    52  		latestTag, err := repeatGetLatestTag(ctx, c, "sqlalchemy", "sqlalchemy", sqlAlchemyReleaseTagRegex)
    53  		if err != nil {
    54  			t.Fatal(err)
    55  		}
    56  		c.l.Printf("Latest sqlalchemy release is %s.", latestTag)
    57  
    58  		// TODO: using the latest version requires the dialect to implement `get_isolation_levels`
    59  		latestTag = "rel_1_3_14"
    60  
    61  		if err := repeatRunE(
    62  			ctx, c, node, "update apt-get",
    63  			`
    64  				sudo add-apt-repository ppa:deadsnakes/ppa &&
    65  				sudo apt-get -qq update`,
    66  		); err != nil {
    67  			t.Fatal(err)
    68  		}
    69  
    70  		if err := repeatRunE(
    71  			ctx,
    72  			c,
    73  			node,
    74  			"install dependencies",
    75  			`sudo apt-get -qq install make python3.7 libpq-dev python3.7-dev gcc python3-setuptools python-setuptools build-essential`,
    76  		); err != nil {
    77  			t.Fatal(err)
    78  		}
    79  
    80  		if err := repeatRunE(
    81  			ctx, c, node, "set python3.7 as default", `
    82  				sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.5 1
    83   				sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 2
    84   				sudo update-alternatives --config python3`,
    85  		); err != nil {
    86  			t.Fatal(err)
    87  		}
    88  
    89  		if err := repeatRunE(
    90  			ctx, c, node, "install pip",
    91  			`curl https://bootstrap.pypa.io/get-pip.py | sudo -H python3.7`,
    92  		); err != nil {
    93  			t.Fatal(err)
    94  		}
    95  
    96  		if err := repeatRunE(
    97  			ctx,
    98  			c,
    99  			node,
   100  			"install pytest",
   101  			// Unpin pytest once sqlalchemy rel_1_3_16 is released (https://github.com/sqlalchemy/sqlalchemy/issues/5201)
   102  			`sudo pip3 install --upgrade --force-reinstall setuptools pytest==5.3.5 pytest-xdist psycopg2`,
   103  		); err != nil {
   104  			t.Fatal(err)
   105  		}
   106  
   107  		if err := repeatRunE(
   108  			ctx, c, node, "remove old sqlalchemy-cockroachdb", `sudo rm -rf /mnt/data1/sqlalchemy-cockroachdb`,
   109  		); err != nil {
   110  			t.Fatal(err)
   111  		}
   112  
   113  		if err := repeatGitCloneE(
   114  			ctx,
   115  			t.l,
   116  			c,
   117  			"https://github.com/cockroachdb/sqlalchemy-cockroachdb.git",
   118  			"/mnt/data1/sqlalchemy-cockroachdb",
   119  			"master",
   120  			node,
   121  		); err != nil {
   122  			t.Fatal(err)
   123  		}
   124  
   125  		t.Status("installing sqlalchemy-cockroachdb")
   126  		if err := repeatRunE(
   127  			ctx, c, node, "installing sqlalchemy=cockroachdb",
   128  			`cd /mnt/data1/sqlalchemy-cockroachdb && sudo python3 setup.py install`,
   129  		); err != nil {
   130  			t.Fatal(err)
   131  		}
   132  
   133  		if err := repeatRunE(
   134  			ctx, c, node, "remove old sqlalchemy", `sudo rm -rf /mnt/data1/sqlalchemy`,
   135  		); err != nil {
   136  			t.Fatal(err)
   137  		}
   138  
   139  		if err := repeatGitCloneE(
   140  			ctx,
   141  			t.l,
   142  			c,
   143  			"https://github.com/sqlalchemy/sqlalchemy.git",
   144  			"/mnt/data1/sqlalchemy",
   145  			latestTag,
   146  			node,
   147  		); err != nil {
   148  			t.Fatal(err)
   149  		}
   150  
   151  		t.Status("building sqlalchemy")
   152  		if err := repeatRunE(
   153  			ctx, c, node, "building sqlalchemy", `cd /mnt/data1/sqlalchemy && python3 setup.py build`,
   154  		); err != nil {
   155  			t.Fatal(err)
   156  		}
   157  
   158  		blacklistName, expectedFailures, ignoredlistName, ignoredlist := sqlAlchemyBlacklists.getLists(version)
   159  		if expectedFailures == nil {
   160  			t.Fatalf("No sqlalchemy blacklist defined for cockroach version %s", version)
   161  		}
   162  		c.l.Printf("Running cockroach version %s, using blacklist %s, using ignoredlist %s",
   163  			version, blacklistName, ignoredlistName)
   164  
   165  		t.Status("running sqlalchemy test suite")
   166  		// Note that this is expected to return an error, since the test suite
   167  		// will fail. And it is safe to swallow it here.
   168  		rawResults, _ := c.RunWithBuffer(ctx, t.l, node,
   169  			`cd /mnt/data1/sqlalchemy/ && pytest -s --maxfail=0 `+
   170  				`--dburi=cockroachdb://root@localhost:26257/defaultdb?sslmode=disable `+
   171  				`test/dialect/test_suite.py`)
   172  
   173  		t.Status("collating the test results")
   174  		c.l.Printf("Test Results: %s", rawResults)
   175  
   176  		// Find all the failed and errored tests.
   177  		results := newORMTestsResults()
   178  
   179  		scanner := bufio.NewScanner(bytes.NewReader(rawResults))
   180  		for scanner.Scan() {
   181  			match := sqlAlchemyResultRegex.FindStringSubmatch(scanner.Text())
   182  			if match == nil {
   183  				continue
   184  			}
   185  			test, result := match[1], match[2]
   186  			pass := result == "PASSED" || strings.Contains(result, "failed as expected")
   187  			skipped := result == "SKIPPED"
   188  			results.allTests = append(results.allTests, test)
   189  
   190  			ignoredIssue, expectedIgnored := ignoredlist[test]
   191  			issue, expectedFailure := expectedFailures[test]
   192  			switch {
   193  			case expectedIgnored:
   194  				results.results[test] = fmt.Sprintf("--- SKIP: %s due to %s (expected)", test, ignoredIssue)
   195  				results.ignoredCount++
   196  			case skipped && expectedFailure:
   197  				results.results[test] = fmt.Sprintf("--- SKIP: %s (unexpected)", test)
   198  				results.unexpectedSkipCount++
   199  			case skipped:
   200  				results.results[test] = fmt.Sprintf("--- SKIP: %s (expected)", test)
   201  				results.skipCount++
   202  			case pass && !expectedFailure:
   203  				results.results[test] = fmt.Sprintf("--- PASS: %s (expected)", test)
   204  				results.passExpectedCount++
   205  			case pass && expectedFailure:
   206  				results.results[test] = fmt.Sprintf("--- PASS: %s - %s (unexpected)",
   207  					test, maybeAddGithubLink(issue),
   208  				)
   209  				results.passUnexpectedCount++
   210  			case !pass && expectedFailure:
   211  				results.results[test] = fmt.Sprintf("--- FAIL: %s - %s (expected)",
   212  					test, maybeAddGithubLink(issue),
   213  				)
   214  				results.failExpectedCount++
   215  				results.currentFailures = append(results.currentFailures, test)
   216  			case !pass && !expectedFailure:
   217  				results.results[test] = fmt.Sprintf("--- FAIL: %s (unexpected)", test)
   218  				results.failUnexpectedCount++
   219  				results.currentFailures = append(results.currentFailures, test)
   220  			}
   221  			results.runTests[test] = struct{}{}
   222  		}
   223  
   224  		results.summarizeAll(
   225  			t, "sqlalchemy" /* ormName */, blacklistName, expectedFailures, version, latestTag)
   226  	}
   227  
   228  	r.Add(testSpec{
   229  		Name:       "sqlalchemy",
   230  		Owner:      OwnerAppDev,
   231  		Cluster:    makeClusterSpec(1),
   232  		MinVersion: "v2.1.0",
   233  		Tags:       []string{`default`, `orm`},
   234  		Run: func(ctx context.Context, t *test, c *cluster) {
   235  			runSQLAlchemy(ctx, t, c)
   236  		},
   237  	})
   238  }