Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Plugin fixtures #716

Merged
merged 22 commits into from
Oct 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 15 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
hooks:
- id: yapf
language: system
files: '^(aiida/control/)|(aiida/cmdline/tests)|(docs/update_req_for_rtd.py)|(.travis_data/test_setup.py)'
files: '^(aiida/control/)|(aiida/cmdline/tests)|(docs/update_req_for_rtd.py)|(\.travis_data/test_setup.py)|(aiida/utils/.*fixtures.py)'

# prospector: collection of linters
- repo: git://github.com/guykisel/prospector-mirror
Expand All @@ -13,7 +13,7 @@
- id: prospector
language: system
exclude: '^(tests/)|(examples/)'
files: '^(aiida/control/)|(aiida/cmdline/tests)|(docs/update_req_for_rtd.py)|(.travis_data/test_setup.py)'
files: '^(aiida/control/)|(aiida/cmdline/tests)|(docs/update_req_for_rtd.py)|(\.travis_data/test_setup.py)|(aiida/utils/.*fixtures.py)'

- repo: local
hooks:
Expand All @@ -24,3 +24,16 @@
files: '^(setup_requirements.py)|(docs/requirements_for_rtd.txt)|(docs/update_req_for_rtd.py)'
pass_filenames: false

- repo: git://github.com/pre-commit/pre-commit-hooks
sha: v0.9.4
hooks:
- id: check-yaml

- repo: local
hooks:
- id: travis-linter
name: travis
entry: travis lint
files: .travis.yml
language: ruby
additional_dependencies: ['travis']
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ known-third-party=enchant
max-args=6

# Maximum number of attributes for a class (see R0902).
max-attributes=7
max-attributes=12

# Maximum number of boolean expressions in a if statement
max-bool-expr=5
Expand Down
82 changes: 82 additions & 0 deletions .travis-data/test_fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""Unittests for plugin test fixture manager"""
import os
import unittest

from pgtest import pgtest

from aiida.utils.fixtures import FixtureManager, FixtureError
from aiida.utils.capturing import Capturing
from aiida.backends.profile import BACKEND_DJANGO, BACKEND_SQLA


class FixtureManagerTestCase(unittest.TestCase):
"""Test the FixtureManager class"""

def setUp(self):
self.fixture_manager = FixtureManager()
self.backend = BACKEND_DJANGO if os.environ.get(
'TEST_AIIDA_BACKEND', BACKEND_DJANGO) == BACKEND_DJANGO else BACKEND_SQLA
self.fixture_manager.backend = self.backend

def test_create_db_cluster(self):
self.fixture_manager.create_db_cluster()
self.assertTrue(
pgtest.is_server_running(self.fixture_manager.pg_cluster.cluster))

def test_create_aiida_db(self):
self.fixture_manager.create_db_cluster()
self.fixture_manager.create_aiida_db()
self.assertTrue(
self.fixture_manager.postgres.db_exists(
self.fixture_manager.db_name))

def test_create_use_destroy_profile(self):
"""
Test temporary test profile creation

* The profile gets created, the dbenv loaded
* Data can be stored in the db
* reset_db deletes all data added after profile creation
* destroy_all removes all traces of the test run
"""
from aiida.common import setup as aiida_cfg
from aiida import is_dbenv_loaded
with Capturing() as output:
self.fixture_manager.create_profile()
self.assertTrue(self.fixture_manager.root_dir_ok, msg=output)
self.assertTrue(self.fixture_manager.config_dir_ok, msg=output)
self.assertTrue(self.fixture_manager.repo_ok, msg=output)
self.assertEqual(
aiida_cfg.AIIDA_CONFIG_FOLDER,
self.fixture_manager.config_dir,
msg=output)
self.assertTrue(is_dbenv_loaded())

from aiida.orm import DataFactory, load_node
data = DataFactory('parameter')(dict={'key': 'value'})
data.store()
data_pk = data.pk
self.assertTrue(load_node(data_pk))

with self.assertRaises(FixtureError):
self.test_create_aiida_db()

self.fixture_manager.reset_db()
with self.assertRaises(Exception):
load_node(data_pk)

temp_dir = self.fixture_manager.root_dir
self.fixture_manager.destroy_all()
with self.assertRaises(Exception):
self.fixture_manager.postgres.db_exists(
self.fixture_manager.db_name)
self.assertFalse(os.path.exists(temp_dir))
self.assertIsNone(self.fixture_manager.root_dir)
self.assertIsNone(self.fixture_manager.pg_cluster)

def tearDown(self):
self.fixture_manager.destroy_all()


if __name__ == '__main__':
unittest.main()
42 changes: 42 additions & 0 deletions .travis-data/test_plugin_testcase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""
Test the plugin test case

This must be in a standalone script because it would clash with other tests,
Since the dbenv gets loaded on the temporary profile.
"""
import os
import unittest

from aiida.utils.fixtures import PluginTestCase
from aiida.backends.profile import BACKEND_DJANGO, BACKEND_SQLA
from aiida import is_dbenv_loaded


def determine_backend():
return BACKEND_DJANGO if os.environ.get(
'TEST_AIIDA_BACKEND', BACKEND_DJANGO) == BACKEND_DJANGO else BACKEND_SQLA


class PluginTestcaseTestCase(PluginTestCase):
BACKEND = determine_backend()

def setUp(self):
from aiida.orm import DataFactory
self.data = DataFactory('parameter')(dict={'data': 'test'})
self.data.store()
self.data_pk = self.data.pk

def test_data_loaded(self):
from aiida.orm import load_node
self.assertTrue(is_dbenv_loaded())
load_node(self.data_pk)

def test_tear_down(self):
from aiida.orm import load_node
super(PluginTestcaseTestCase, self).tearDown()
with self.assertRaises(Exception):
load_node(self.data_pk)


if __name__ == '__main__':
unittest.main()
2 changes: 2 additions & 0 deletions .travis-data/test_script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ case "$TEST_TYPE" in
tests)
# Run the AiiDA tests
python ${TRAVIS_BUILD_DIR}/.travis-data/test_setup.py
python ${TRAVIS_BUILD_DIR}/.travis-data/test_fixtures.py
python ${TRAVIS_BUILD_DIR}/.travis-data/test_plugin_testcase.py

verdi -p test_$TEST_AIIDA_BACKEND devel tests

Expand Down
2 changes: 0 additions & 2 deletions aiida/backends/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,5 +283,3 @@ def _get_column(colname, alias):
'\n'.join(alias._sa_class_manager.mapper.c.keys())
)
)


49 changes: 29 additions & 20 deletions aiida/cmdline/commands/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def complete_emails(self, subargs_idx, subargs):
return "\n".join(emails)


def do_configure(email, first_name, last_name, institution, no_password):
def do_configure(email, first_name, last_name, institution, no_password, non_interactive=False, force=False, **kwargs):
if not is_dbenv_loaded():
load_dbenv()

Expand All @@ -65,32 +65,41 @@ def do_configure(email, first_name, last_name, institution, no_password):
if created:
click.echo("\nAn AiiDA user for email '{}' is already present "
"in the DB:".format(email))
reply = click.confirm("Do you want to reconfigure it?")
if reply:
if not non_interactive:
reply = click.confirm("Do you want to reconfigure it?")
if reply:
configure_user = True
elif force:
configure_user = True
else:
configure_user = True
click.echo("Configuring a new user with email '{}'".format(email))

if configure_user:
try:
kwargs = {}
for field in UserModel.REQUIRED_FIELDS:
verbose_name = field.capitalize()
readline.set_startup_hook(lambda: readline.insert_text(
getattr(user, field)))
if field == 'first_name' and first_name:
kwargs[field] = first_name
elif field == 'last_name' and last_name:
kwargs[field] = last_name
elif field == 'institution' and institution:
kwargs[field] = institution
else:
kwargs[field] = raw_input('{}: '.format(verbose_name))
finally:
readline.set_startup_hook(lambda: readline.insert_text(""))
if not non_interactive:
try:
attributes = {}
for field in UserModel.REQUIRED_FIELDS:
verbose_name = field.capitalize()
readline.set_startup_hook(lambda: readline.insert_text(
getattr(user, field)))
if field == 'first_name' and first_name:
attributes[field] = first_name
elif field == 'last_name' and last_name:
attributes[field] = last_name
elif field == 'institution' and institution:
attributes[field] = institution
else:
attributes[field] = raw_input('{}: '.format(verbose_name))
finally:
readline.set_startup_hook(lambda: readline.insert_text(""))
else:
attributes = kwargs.copy()
attributes['first_name'] = first_name
attributes['last_name'] = last_name
attributes['institution'] = institution

for k, v in kwargs.iteritems():
for k, v in attributes.iteritems():
setattr(user, k, v)

change_password = False
Expand Down
12 changes: 9 additions & 3 deletions aiida/cmdline/verdilib.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,9 +592,15 @@ def setup(profile, only_config, non_interactive=False, **kwargs):
user.configure.main(args=[email])
else:
# or don't ask
aiida.cmdline.commands.user.do_configure(kwargs['email'], kwargs.get('first_name'),
kwargs.get('last_name'), kwargs.get('institution'),
True)
aiida.cmdline.commands.user.do_configure(
email=kwargs['email'],
first_name=kwargs.get('first_name'),
last_name=kwargs.get('last_name'),
institution=kwargs.get('institution'),
no_password=True,
non_interactive=non_interactive,
force=True
)

print "Setup finished."

Expand Down
9 changes: 8 additions & 1 deletion aiida/control/postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
_GRANT_PRIV_COMMAND = 'GRANT ALL PRIVILEGES ON DATABASE "{}" TO "{}"'
_GET_USERS_COMMAND = "SELECT usename FROM pg_user WHERE usename='{}'"
_CHECK_DB_EXISTS_COMMAND = "SELECT datname FROM pg_database WHERE datname='{}'"
_COPY_DB_COMMAND = 'CREATE DATABASE "{}" WITH TEMPLATE "{}" OWNER "{}"'


class Postgres(object):
Expand Down Expand Up @@ -157,6 +158,10 @@ def drop_db(self, dbname):
"""
self.pg_execute(_DROP_DB_COMMAND.format(dbname), **self.dbinfo)

def copy_db(self, src_db, dest_db, dbuser):
self.pg_execute(
_COPY_DB_COMMAND.format(dest_db, src_db, dbuser), **self.dbinfo)

def db_exists(self, dbname):
"""
Check wether a postgres database with dbname exists
Expand Down Expand Up @@ -240,8 +245,9 @@ def _try_connect(**kwargs):
from psycopg2 import connect
success = False
try:
connect(**kwargs)
conn = connect(**kwargs)
success = True
conn.close()
except Exception: # pylint: disable=broad-except
pass
return success
Expand Down Expand Up @@ -281,6 +287,7 @@ def _pg_execute_psyco(command, **kwargs):
output = cur.fetchall()
except ProgrammingError:
pass
conn.close()
return output


Expand Down