# Copyright 2019 TerraPower, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Provides functionality for testing implementations of plugins."""
import unittest
from copy import deepcopy
from typing import Optional
import yamlize
from armi import context
from armi import getApp
from armi import getPluginManagerOrFail
from armi import interfaces
from armi import plugins
from armi import settings
from armi import utils
from armi.physics.neutronics import NeutronicsPlugin
from armi.reactor.blocks import Block
from armi.reactor.flags import Flags
from armi.reactor.tests.test_reactors import loadTestReactor, TEST_ROOT
[docs]class PluginFlags1(plugins.ArmiPlugin):
"""Simple Plugin that defines a single, new flag."""
[docs] @staticmethod
@plugins.HOOKIMPL
def defineFlags():
"""Function to provide new Flags definitions."""
return {"SUPER_FLAG": utils.flags.auto()}
[docs]class TestPluginRegistration(unittest.TestCase):
def setUp(self):
"""
Manipulate the standard App. We can't just configure our own, since the
pytest environment bleeds between tests.
"""
self._backupApp = deepcopy(getApp())
def tearDown(self):
"""Restore the App to its original state."""
import armi
armi._app = self._backupApp
context.APP_NAME = "armi"
[docs] def test_defineFlags(self):
"""Define a new flag using the plugin defineFlags() method.
.. test:: Define a new, unique flag through the plugin pathway.
:id: T_ARMI_FLAG_EXTEND1
:tests: R_ARMI_FLAG_EXTEND
.. test:: Load a plugin into an app and show it is loaded.
:id: T_ARMI_PLUGIN_REGISTER
:tests: R_ARMI_PLUGIN
"""
app = getApp()
# show the new plugin isn't loaded yet
pluginNames = [p[0] for p in app.pluginManager.list_name_plugin()]
self.assertNotIn("PluginFlags1", pluginNames)
# show the flag doesn't exist yet
with self.assertRaises(AttributeError):
Flags.SUPER_FLAG
# load the plugin
app.pluginManager.register(PluginFlags1)
# show the new plugin is loaded now
pluginNames = [p[0] for p in app.pluginManager.list_name_plugin()]
self.assertIn("PluginFlags1", pluginNames)
# force-register new flags from the new plugin
app._pluginFlagsRegistered = False
app.registerPluginFlags()
# show the flag exists now
self.assertEqual(type(Flags.SUPER_FLAG._value), int)
[docs]class TestPluginBasics(unittest.TestCase):
[docs] def test_defineParameters(self):
"""Test that the default ARMI plugins are correctly defining parameters.
.. test:: ARMI plugins define parameters, which appear on a new Block.
:id: T_ARMI_PLUGIN_PARAMS
:tests: R_ARMI_PLUGIN_PARAMS
"""
# create a block
b = Block("fuel", height=10.0)
# unless a plugin has registerd a param, it doesn't exist
with self.assertRaises(AttributeError):
b.p.fakeParam
# Check the default values of parameters defined by the neutronics plugin
self.assertIsNone(b.p.axMesh)
self.assertEqual(b.p.flux, 0)
self.assertEqual(b.p.power, 0)
self.assertEqual(b.p.pdens, 0)
# Check the default values of parameters defined by the fuel peformance plugin
self.assertEqual(b.p.gasPorosity, 0)
self.assertEqual(b.p.liquidPorosity, 0)
[docs] def test_exposeInterfaces(self):
"""Make sure that the exposeInterfaces hook is properly implemented.
.. test:: Plugins can add interfaces to the interface stack.
:id: T_ARMI_PLUGIN_INTERFACES0
:tests: R_ARMI_PLUGIN_INTERFACES
"""
plugin = NeutronicsPlugin()
cs = settings.Settings()
results = plugin.exposeInterfaces(cs)
# each plugin should return a list
self.assertIsInstance(results, list)
self.assertGreater(len(results), 0)
for result in results:
# Make sure all elements in the list satisfy the constraints of the hookspec
self.assertIsInstance(result, tuple)
self.assertEqual(len(result), 3)
order, interface, kwargs = result
self.assertIsInstance(order, (int, float))
self.assertTrue(issubclass(interface, interfaces.Interface))
self.assertIsInstance(kwargs, dict)
[docs] def test_pluginsExposeInterfaces(self):
"""Make sure that plugins properly expose their interfaces, by checking some
known examples.
.. test:: Check that some known plugins correctly add interfaces to the stack.
:id: T_ARMI_PLUGIN_INTERFACES1
:tests: R_ARMI_PLUGIN_INTERFACES
"""
# generate a test operator, with a full set of interfaces from plugsin
o = loadTestReactor(TEST_ROOT)[0]
pm = getPluginManagerOrFail()
# test the plugins were generated
plugins = pm.get_plugins()
self.assertGreater(len(plugins), 0)
# test interfaces were generated from those plugins
ints = o.interfaces
self.assertGreater(len(ints), 0)
# test that certain plugins exist and correctly registered their interfaces
pluginStrings = " ".join([str(p) for p in plugins])
interfaceStrings = " ".join([str(i) for i in ints])
# Test that the BookkeepingPlugin registered the DatabaseInterface
self.assertIn("BookkeepingPlugin", pluginStrings)
self.assertIn("DatabaseInterface", interfaceStrings)
# Test that the BookkeepingPlugin registered the history interface
self.assertIn("BookkeepingPlugin", pluginStrings)
self.assertIn("history", interfaceStrings)
# Test that the EntryPointsPlugin registered the main interface
self.assertIn("EntryPointsPlugin", pluginStrings)
self.assertIn("main", interfaceStrings)
# Test that the FuelHandlerPlugin registered the fuelHandler interface
self.assertIn("FuelHandlerPlugin", pluginStrings)
self.assertIn("fuelHandler", interfaceStrings)
[docs]class TestPlugin(unittest.TestCase):
"""This contains some sanity tests that can be used by implementing plugins."""
plugin: Optional[plugins.ArmiPlugin] = None
[docs] def test_defineBlueprintsSections(self):
"""Make sure that the defineBlueprintsSections hook is properly implemented."""
if self.plugin is None:
return
if not hasattr(self.plugin, "defineBlueprintsSections"):
return
results = self.plugin.defineBlueprintsSections()
if results is None:
return
# each plugin should return a list
self.assertIsInstance(results, (list, type(None)))
for result in results:
self.assertIsInstance(result, tuple)
self.assertEqual(len(result), 3)
self.assertIsInstance(result[0], str)
self.assertIsInstance(result[1], yamlize.Attribute)
self.assertTrue(callable(result[2]))
[docs] def test_exposeInterfaces(self):
"""Make sure that the exposeInterfaces hook is properly implemented."""
if self.plugin is None:
return
cs = settings.Settings()
results = self.plugin.exposeInterfaces(cs)
if results is None or not results:
return
# each plugin should return a list
self.assertIsInstance(results, list)
for result in results:
# Make sure all elements in the list satisfy the constraints of the hookspec
self.assertIsInstance(result, tuple)
self.assertEqual(len(result), 3)
order, interface, kwargs = result
self.assertIsInstance(order, (int, float))
self.assertTrue(issubclass(interface, interfaces.Interface))
self.assertIsInstance(kwargs, dict)