# -*- coding: utf-8 -*-
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved. #
# This file is part of the AiiDA code. #
# #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida_core #
# For further information on the license, see the LICENSE.txt file #
# For further information please visit http://www.aiida.net #
###########################################################################
import os
import sys
import unittest
from unittest import (
TestSuite, defaultTestLoader as test_loader)
from aiida.backends import settings
from aiida.backends.tests import get_db_test_list
from aiida.common.exceptions import ConfigurationError, TestsNotAllowedError, InternalError
from aiida.common.utils import classproperty
[docs]def check_if_tests_can_run():
"""
Check if the tests can run (i.e., if we are in a test profile).
Otherwise, raise TestsNotAllowedError.
"""
from aiida import settings as settings2
from aiida.common.setup import TEST_KEYWORD
base_repo_path = os.path.basename(
os.path.normpath(settings2.REPOSITORY_PATH))
if (not settings.AIIDADB_PROFILE.startswith(TEST_KEYWORD) or
TEST_KEYWORD not in base_repo_path or
not settings2.DBNAME.startswith(TEST_KEYWORD)):
msg = [
"A non-test profile was given for tests. Please note "
"that the test profile should have test specific "
"database name and test specific repository name.",
"Given profile: {}".format(settings.AIIDADB_PROFILE),
"Related repository path: {}".format(base_repo_path),
"Related database name: {}".format(settings2.DBNAME)]
raise TestsNotAllowedError("\n".join(msg))
[docs]class AiidaTestCase(unittest.TestCase):
"""
This is the base class for AiiDA tests, independent of the backend.
Internally it loads the AiidaTestImplementation subclass according to the current backend
"""
_class_was_setup = False
__backend_instance = None
[docs] @classmethod
def get_backend_class(cls):
from aiida.backends.testimplbase import AiidaTestImplementation
from aiida.backends.profile import BACKEND_SQLA, BACKEND_DJANGO
# Freeze the __impl_class after the first run
if not hasattr(cls, '__impl_class'):
if settings.BACKEND == BACKEND_SQLA:
from aiida.backends.sqlalchemy.tests.testbase import (
SqlAlchemyTests)
cls.__impl_class = SqlAlchemyTests
elif settings.BACKEND == BACKEND_DJANGO:
from aiida.backends.djsite.db.testbase import DjangoTests
cls.__impl_class = DjangoTests
else:
raise ConfigurationError("Unknown backend type")
# Check that it is of the right class
if not issubclass(cls.__impl_class, AiidaTestImplementation):
raise InternalError("The AiiDA test implementation is not of type "
"{}, that is not a subclass of AiidaTestImplementation".format(
cls.__impl_class.__name__
))
return cls.__impl_class
[docs] @classmethod
def setUpClass(cls, *args, **kwargs):
# Note: this will raise an exception, that will be seen as a test
# failure. To be safe, you should do the same check also in the tearDownClass
# to avoid that it is run
check_if_tests_can_run()
cls.__backend_instance = cls.get_backend_class()()
cls.__backend_instance.setUpClass_method(*args, **kwargs)
cls._class_was_setup = True
[docs] def setUp(self):
self.__backend_instance.setUp_method()
[docs] def tearDown(self):
self.__backend_instance.tearDown_method()
[docs] @classmethod
def insert_data(cls):
cls.__backend_instance.insert_data()
[docs] @classmethod
def clean_db(cls):
# Note: this will raise an exception, that will be seen as a test
# failure. To be safe, you should do the same check also in the tearDownClass
# to avoid that it is run
check_if_tests_can_run()
from aiida.common.exceptions import InvalidOperation
if not cls._class_was_setup:
raise InvalidOperation("You cannot call clean_db before running the setUpClass")
cls.__backend_instance.clean_db()
@classproperty
def computer(cls):
return cls.__backend_instance.get_computer()
@classproperty
def user_email(cls):
return cls.__backend_instance.get_user_email()
[docs] @classmethod
def tearDownClass(cls, *args, **kwargs):
# Double check for double security to avoid to run the tearDown
# if this is not a test profile
check_if_tests_can_run()
cls.__backend_instance.tearDownClass_method(*args, **kwargs)
[docs]def run_aiida_db_tests(tests_to_run, verbose=False):
"""
Run all tests specified in tests_to_run.
Return the list of test results.
"""
# Empty test suite that will be populated
test_suite = TestSuite()
actually_run_tests = []
num_tests_expected = 0
# To avoid adding more than once the same test
# (e.g. if you type both db and db.xxx)
found_modulenames = set()
for test in set(tests_to_run):
try:
modulenames = get_db_test_list()[test]
except KeyError:
if verbose:
print >> sys.stderr, "Unknown DB test {}... skipping".format(
test)
continue
actually_run_tests.append(test)
for modulename in modulenames:
if modulename not in found_modulenames:
try:
test_suite.addTest(test_loader.loadTestsFromName(modulename))
except AttributeError as exception:
try:
import importlib
import traceback
importlib.import_module(modulename)
except ImportError as exception:
print >> sys.stderr, (
"[CRITICAL] The module '{}' has an import error and the tests cannot be run:\n{}"
.format(modulename, traceback.format_exc(exception))
)
sys.exit(1)
found_modulenames.add(modulename)
num_tests_expected = test_suite.countTestCases()
if verbose:
print >> sys.stderr, (
"DB tests that will be run: {} (expecting {} tests)".format(
",".join(actually_run_tests), num_tests_expected))
results = unittest.TextTestRunner(failfast=False).run(test_suite)
if verbose:
print "Run tests: {}".format(results.testsRun)
return results