# -*- 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 #
###########################################################################
"""Tests for utilities dealing with plugins and entry points."""
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
from aiida import __version__ as version_core
from aiida.backends.testbase import AiidaTestCase
from aiida.engine import calcfunction, WorkChain
from aiida.plugins import CalculationFactory
from aiida.plugins.utils import PluginVersionProvider
[docs]class TestPluginVersionProvider(AiidaTestCase):
"""Tests for the :py:class:`~aiida.plugins.utils.PluginVersionProvider` utility class."""
# pylint: disable=no-init,old-style-class,too-few-public-methods,no-member
[docs] def setUp(self):
super(TestPluginVersionProvider, self).setUp()
self.provider = PluginVersionProvider()
[docs] @staticmethod
def create_dynamic_plugin_module(plugin, plugin_version, add_module_to_sys=True, add_version=True):
"""Create a fake dynamic module with a certain `plugin` entity, a class or function and the given version."""
import sys
import types
import uuid
# Create a new module with a unique name and add the `plugin` and `plugin_version` as attributes
module_name = 'TestModule{}'.format(str(uuid.uuid4())[:5])
dynamic_module = types.ModuleType(module_name, 'Dynamically created module for testing purposes')
setattr(plugin, '__module__', dynamic_module.__name__)
setattr(dynamic_module, plugin.__name__, plugin)
# For tests that need to fail, this flag can be set to `False`
if add_version:
setattr(dynamic_module, '__version__', plugin_version)
# Get the `DummyClass` plugin from the dynamically created test module
dynamic_plugin = getattr(dynamic_module, plugin.__name__)
# Make the dynamic module importable unless the test requests not to, to test an unimportable module
if add_module_to_sys:
sys.modules[dynamic_module.__name__] = dynamic_module
return dynamic_plugin
[docs] def test_external_module_import_fail(self):
"""Test that mapper does not except even if external module cannot be imported."""
class DummyCalcJob():
pass
version_plugin = '0.1.01'
dynamic_plugin = self.create_dynamic_plugin_module(DummyCalcJob, version_plugin, add_module_to_sys=False)
expected_version = {'version': {'core': version_core}}
self.assertEqual(self.provider.get_version_info(dynamic_plugin), expected_version)
[docs] def test_external_module_no_version_attribute(self):
"""Test that mapper does not except even if external module does not define `__version__` attribute."""
class DummyCalcJob():
pass
version_plugin = '0.1.02'
dynamic_plugin = self.create_dynamic_plugin_module(DummyCalcJob, version_plugin, add_version=False)
expected_version = {'version': {'core': version_core}}
self.assertEqual(self.provider.get_version_info(dynamic_plugin), expected_version)
[docs] def test_external_module_class(self):
"""Test the mapper works for a class from an external module."""
class DummyCalcJob():
pass
version_plugin = '0.1.17'
dynamic_plugin = self.create_dynamic_plugin_module(DummyCalcJob, version_plugin)
expected_version = {'version': {'core': version_core, 'plugin': version_plugin}}
self.assertEqual(self.provider.get_version_info(dynamic_plugin), expected_version)
[docs] def test_external_module_function(self):
"""Test the mapper works for a function from an external module."""
@calcfunction
def test_calcfunction():
return
version_plugin = '0.2.19'
dynamic_plugin = self.create_dynamic_plugin_module(test_calcfunction, version_plugin)
expected_version = {'version': {'core': version_core, 'plugin': version_plugin}}
self.assertEqual(self.provider.get_version_info(dynamic_plugin), expected_version)
[docs] def test_calcfunction(self):
"""Test the mapper for a `calcfunction`."""
@calcfunction
def test_calcfunction():
return
expected_version = {'version': {'core': version_core, 'plugin': version_core}}
self.assertEqual(self.provider.get_version_info(test_calcfunction), expected_version)
[docs] def test_calc_job(self):
"""Test the mapper for a `CalcJob`."""
AddArithmeticCalculation = CalculationFactory('arithmetic.add') # pylint: disable=invalid-name
expected_version = {'version': {'core': version_core, 'plugin': version_core}}
self.assertEqual(self.provider.get_version_info(AddArithmeticCalculation), expected_version)
[docs] def test_work_chain(self):
"""Test the mapper for a `WorkChain`."""
class SomeWorkChain(WorkChain):
"""Need to create a dummy class since there is no built-in work chain with entry point in `aiida-core`."""
expected_version = {'version': {'core': version_core, 'plugin': version_core}}
self.assertEqual(self.provider.get_version_info(SomeWorkChain), expected_version)