]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- proof of concept for parallel testing
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 25 Jul 2014 22:33:04 +0000 (18:33 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 25 Jul 2014 22:33:04 +0000 (18:33 -0400)
lib/sqlalchemy/engine/url.py
lib/sqlalchemy/testing/config.py
lib/sqlalchemy/testing/fixtures.py
lib/sqlalchemy/testing/plugin/plugin_base.py
lib/sqlalchemy/testing/plugin/provision.py [new file with mode: 0644]
lib/sqlalchemy/testing/plugin/pytestplugin.py

index e3629613fe7aba375b0f92721efd30194be5e279..116a3a343ae07efc1f15ba7a856313e38500e05c 100644 (file)
@@ -105,6 +105,12 @@ class URL(object):
             self.database == other.database and \
             self.query == other.query
 
+    def get_backend_name(self):
+        if '+' not in self.drivername:
+            return self.drivername
+        else:
+            return self.drivername.split('+')[0]
+
     def get_dialect(self):
         """Return the SQLAlchemy database dialect class corresponding
         to this URL's driver name.
index c914434b4f8a8b69714b71db928a51c1af93e1f0..84344eb311151eeb4e98aa7f17f009a671dc4f1f 100644 (file)
@@ -78,3 +78,4 @@ class Config(object):
     def all_dbs(cls):
         for cfg in cls.all_configs():
             yield cfg.db
+
index 7c7b009986c86f2cde7eb04712fd287f689e9731..d86049da749591b6ff598e697c10c9246a926be2 100644 (file)
@@ -91,20 +91,12 @@ class TablesTest(TestBase):
                 cls.run_create_tables = 'each'
             assert cls.run_inserts in ('each', None)
 
-        if cls.other is None:
-            cls.other = adict()
+        cls.other = adict()
+        cls.tables = adict()
 
-        if cls.tables is None:
-            cls.tables = adict()
-
-        if cls.bind is None:
-            setattr(cls, 'bind', cls.setup_bind())
-
-        if cls.metadata is None:
-            setattr(cls, 'metadata', sa.MetaData())
-
-        if cls.metadata.bind is None:
-            cls.metadata.bind = cls.bind
+        cls.bind = cls.setup_bind()
+        cls.metadata = sa.MetaData()
+        cls.metadata.bind = cls.bind
 
     @classmethod
     def _setup_once_inserts(cls):
index 2590f3b1ee57ddaecf6ee4e9427815560315cbaf..47d297b2eb4339e8146cfe4872de6f3af4e85056 100644 (file)
@@ -31,6 +31,7 @@ if py3k:
 else:
     import ConfigParser as configparser
 
+FOLLOWER_IDENT = None
 
 # late imports
 fixtures = None
@@ -100,6 +101,11 @@ def setup_options(make_option):
                 help="Write/update profiling data.")
 
 
+def configure_follower(follower_ident):
+    global FOLLOWER_IDENT
+    FOLLOWER_IDENT = "test_%s" % follower_ident
+
+
 def read_config():
     global file_config
     file_config = configparser.ConfigParser()
@@ -135,6 +141,7 @@ def post_begin():
     from sqlalchemy import util
 
 
+
 def _log(opt_str, value, parser):
     global logging
     if not logging:
@@ -176,6 +183,7 @@ def post(fn):
     return fn
 
 
+
 @pre
 def _setup_options(opt, file_config):
     global options
@@ -214,6 +222,10 @@ def _engine_uri(options, file_config):
         db_urls.append(file_config.get('db', 'default'))
 
     for db_url in db_urls:
+        if FOLLOWER_IDENT:
+            from sqlalchemy.engine import url
+            db_url = url.make_url(db_url)
+            db_url.database = FOLLOWER_IDENT
         eng = engines.testing_engine(db_url, db_opts)
         eng.connect().close()
         config.Config.register(eng, db_opts, options, file_config, testing)
diff --git a/lib/sqlalchemy/testing/plugin/provision.py b/lib/sqlalchemy/testing/plugin/provision.py
new file mode 100644 (file)
index 0000000..d665727
--- /dev/null
@@ -0,0 +1,62 @@
+from sqlalchemy.engine import url as sa_url
+
+def create_follower_db(follower_ident):
+    from .. import config, engines
+
+    follower_ident = "test_%s" % follower_ident
+
+    hosts = set()
+
+    for cfg in config.Config.all_configs():
+        url = cfg.db.url
+        backend = url.get_backend_name()
+        host_conf = (
+            backend,
+            url.username, url.host, url.database)
+
+        if host_conf not in hosts:
+            if backend.startswith("postgresql"):
+                _pg_create_db(cfg.db, follower_ident)
+            elif backend.startswith("mysql"):
+                _mysql_create_db(cfg.db, follower_ident)
+
+            new_url = sa_url.make_url(str(url))
+
+            new_url.database = follower_ident
+            eng = engines.testing_engine(new_url, cfg.db_opts)
+
+            if backend.startswith("postgresql"):
+                _pg_init_db(eng)
+            elif backend.startswith("mysql"):
+                _mysql_init_db(eng)
+
+            hosts.add(host_conf)
+
+
+def _pg_create_db(eng, ident):
+    with eng.connect().execution_options(
+            isolation_level="AUTOCOMMIT") as conn:
+        try:
+            conn.execute("DROP DATABASE %s" % ident)
+        except:
+            pass
+        conn.execute("CREATE DATABASE %s" % ident)
+
+
+def _pg_init_db(eng):
+    with eng.connect() as conn:
+        conn.execute("CREATE SCHEMA test_schema")
+        conn.execute("CREATE SCHEMA test_schema_2")
+
+
+def _mysql_create_db(eng, ident):
+    with eng.connect() as conn:
+        try:
+            conn.execute("DROP DATABASE %s" % ident)
+        except:
+            pass
+        conn.execute("CREATE DATABASE %s" % ident)
+
+
+def _mysql_init_db(eng):
+    pass
index 11238bbac40a82d5fe9ebb301fd83a3908afd0ff..7bef644d98d25433d7eaacfc9dd5bc285d36a01b 100644 (file)
@@ -3,6 +3,7 @@ import argparse
 import inspect
 from . import plugin_base
 import collections
+import itertools
 
 
 def pytest_addoption(parser):
@@ -24,6 +25,11 @@ def pytest_addoption(parser):
 
 
 def pytest_configure(config):
+    if hasattr(config, "slaveinput"):
+        plugin_base.configure_follower(
+            config.slaveinput["follower_ident"]
+        )
+
     plugin_base.pre_begin(config.option)
 
     plugin_base.set_coverage_flag(bool(getattr(config.option,
@@ -31,6 +37,16 @@ def pytest_configure(config):
 
     plugin_base.post_begin()
 
+_follower_count = itertools.count(1)
+
+
+def pytest_configure_node(node):
+    # the master for each node fills slaveinput dictionary
+    # which pytest-xdist will transfer to the subprocess
+    node.slaveinput["follower_ident"] = next(_follower_count)
+    from . import provision
+    provision.create_follower_db(node.slaveinput["follower_ident"])
+
 
 def pytest_collection_modifyitems(session, config, items):
     # look for all those classes that specify __backend__ and