Make context plugins decide how to map their data for scenario

Previously to have only one user and its context during the run
of scenario we had hack inside base runner to map full context
for scenario.

This patch makes context classes resposible for that. E.g. you
can implement method that will somehow map resources of context
for each run of scenario.

Fix context.__init__() methods they shouldn't change context
object.

There are 3 great benifits of this change:

- You don't need hacks to map resources created by context
  for scenarios, which will be crucial for other projects

- You don't need hacks to make mapping strategies configurable
  e.g. pick users randomly or 1 by 1 and so on

- Rally framework depends less on OpenStack stuff

Change-Id: Ie5915cd35b27cb1988465ba33179721bf61224c7
This commit is contained in:
Boris Pavlovic
2015-07-20 20:30:54 -07:00
parent a650762a5c
commit 88b4137497
17 changed files with 189 additions and 156 deletions

View File

@@ -63,9 +63,6 @@ class ImageGenerator(context.Context):
"additionalProperties": False
}
def __init__(self, ctx):
super(ImageGenerator, self).__init__(ctx)
@rutils.log_task_wrapper(LOG.info, _("Enter context: `Images`"))
def setup(self):
image_url = self.config["image_url"]

View File

@@ -18,6 +18,7 @@ from rally.common import log as logging
from rally.common import objects
from rally.common import utils as rutils
from rally import osclients
from rally.plugins.openstack.context.keystone import users
from rally.task import context
@@ -27,8 +28,9 @@ LOG = logging.getLogger(__name__)
# NOTE(boris-42): This context should be hidden for now and used only by
# benchmark engine. In future during various refactoring of
# validation system and rally CI testing we will make it public
@context.configure(name="existing_users", order=99, hidden=True)
class ExistingUsers(context.Context):
class ExistingUsers(users.UserContextMixin, context.Context):
"""This context supports using existing users in Rally.
It uses information about deployment to properly
@@ -42,13 +44,11 @@ class ExistingUsers(context.Context):
# this is used only by benchmark engine
CONFIG_SCHEMA = {}
def __init__(self, ctx):
super(ExistingUsers, self).__init__(ctx)
@rutils.log_task_wrapper(LOG.info, _("Enter context: `existing_users`"))
def setup(self):
self.context["users"] = []
self.context["tenants"] = {}
@rutils.log_task_wrapper(LOG.info, _("Enter context: `existing_users`"))
def setup(self):
for user in self.config:
user_endpoint = objects.Endpoint(**user)
user_kclient = osclients.Clients(user_endpoint).keystone()

View File

@@ -40,7 +40,6 @@ class RoleGenerator(context.Context):
def __init__(self, ctx):
super(RoleGenerator, self).__init__(ctx)
self.context["roles"] = []
self.endpoint = self.context["admin"]["endpoint"]
def _add_role(self, admin_endpoint, context_role):
@@ -83,9 +82,8 @@ class RoleGenerator(context.Context):
@rutils.log_task_wrapper(LOG.info, _("Enter context: `roles`"))
def setup(self):
"""Add roles to all users."""
for name in self.config:
role = self._add_role(self.endpoint, name)
self.context["roles"].append(role)
self.context["roles"] = [self._add_role(self.endpoint, name)
for name in self.config]
@rutils.log_task_wrapper(LOG.info, _("Exit context: `roles`"))
def cleanup(self):

View File

@@ -14,9 +14,11 @@
# under the License.
import collections
import random
import uuid
from oslo_config import cfg
import six
from rally.common import broker
from rally.common.i18n import _
@@ -52,8 +54,28 @@ CONF.register_opts(USER_CONTEXT_OPTS,
title="benchmark context options"))
class UserContextMixin(object):
def map_for_scenario(self, context_obj):
"""Pass only context of one user and related to it tenant to scenario.
We are choosing on each iteration one user
"""
scenario_ctx = {}
for key, value in six.iteritems(context_obj):
if key not in ["users", "tenants"]:
scenario_ctx[key] = value
user = random.choice(context_obj["users"])
tenant = context_obj["tenants"][user["tenant_id"]]
scenario_ctx["user"], scenario_ctx["tenant"] = user, tenant
return scenario_ctx
@context.configure(name="users", order=100)
class UserGenerator(context.Context):
class UserGenerator(UserContextMixin, context.Context):
"""Context class for generating temporary users/tenants for benchmarks."""
CONFIG_SCHEMA = {
@@ -95,14 +117,7 @@ class UserGenerator(context.Context):
def __init__(self, context):
super(UserGenerator, self).__init__(context)
self.context["users"] = []
self.context["tenants"] = {}
self.endpoint = self.context["admin"]["endpoint"]
# NOTE(boris-42): I think this is the best place for adding logic when
# we are using pre created users or temporary. So we
# should rename this class s/UserGenerator/UserContext/
# and change a bit logic of populating lists of users
# and tenants
def _remove_default_security_group(self):
"""Delete default security group for tenants."""
@@ -254,6 +269,9 @@ class UserGenerator(context.Context):
@rutils.log_task_wrapper(LOG.info, _("Enter context: `users`"))
def setup(self):
"""Create tenants and users, using the broker pattern."""
self.context["users"] = []
self.context["tenants"] = {}
threads = self.config["resource_management_workers"]
LOG.debug("Creating %(tenants)d tenants using %(threads)s threads" %

View File

@@ -86,12 +86,10 @@ class SaharaCluster(context.Context):
"flavor_id"]
}
def __init__(self, ctx):
super(SaharaCluster, self).__init__(ctx)
self.context["sahara_clusters"] = {}
@rutils.log_task_wrapper(LOG.info, _("Enter context: `Sahara Cluster`"))
def setup(self):
self.context["sahara_clusters"] = {}
wait_dict = {}
for user, tenant_id in rutils.iterate_per_tenants(

View File

@@ -86,8 +86,8 @@ class SaharaEDP(context.Context):
"output_type", "output_url_prefix"]
}
def __init__(self, ctx):
super(SaharaEDP, self).__init__(ctx)
@rutils.log_task_wrapper(LOG.info, _("Enter context: `Sahara EDP`"))
def setup(self):
self.context["sahara_output_conf"] = {
"output_type": self.config["output_type"],
"output_url_prefix": self.config["output_url_prefix"]
@@ -95,9 +95,6 @@ class SaharaEDP(context.Context):
self.context["sahara_mains"] = {}
self.context["sahara_libs"] = {}
@rutils.log_task_wrapper(LOG.info, _("Enter context: `Sahara EDP`"))
def setup(self):
input_type = self.config["input_type"]
input_url = self.config["input_url"]
mains = self.config.get("mains", [])

View File

@@ -58,10 +58,6 @@ class SaharaImage(context.Context):
"additionalProperties": False
}
def __init__(self, ctx):
super(SaharaImage, self).__init__(ctx)
self.context["sahara_images"] = {}
def _create_image(self, hadoop_version, image_url, plugin_name, user,
user_name):
scenario = glance_utils.GlanceScenario({"user": user})
@@ -78,9 +74,10 @@ class SaharaImage(context.Context):
@rutils.log_task_wrapper(LOG.info, _("Enter context: `Sahara Image`"))
def setup(self):
self.context["sahara_images"] = {}
# The user may want to use the existing image. In this case he should
# make sure that the image is public and has all required metadata.
image_uuid = self.config.get("image_uuid")
self.context["need_sahara_image_cleanup"] = not image_uuid

View File

@@ -14,6 +14,7 @@
# under the License.
import abc
import copy
import jsonschema
import six
@@ -84,6 +85,8 @@ class Context(plugin.Plugin, functional.FunctionalMixin):
@classmethod
def validate(cls, config, non_hidden=False):
# TODO(boris-42): This is going to be replaced with common validation
# mechanism (generalization of scenario validation)
if non_hidden and cls._meta_get("hidden"):
raise exceptions.PluginNotFound(name=cls.get_name(),
namespace="context")
@@ -95,11 +98,40 @@ class Context(plugin.Plugin, functional.FunctionalMixin):
@abc.abstractmethod
def setup(self):
"""Set context of benchmark."""
"""Prepare environment for test.
This method is executed only once before load generation.
self.config contains input arguments of this context
self.context contains information that will be passed to scenario
The goal of this method is to perform all operation to prepare
environment and store information to self.context that is required
by scenario.
"""
@abc.abstractmethod
def cleanup(self):
"""Clean context of benchmark."""
"""Clean up environment after load generation.
This method is run once after load generation is done to cleanup
environment.
self.config contains input arguments of this context
self.context contains information that was passed to scenario
"""
def map_for_scenario(self, context_obj):
"""Maps global context to context that is used by scenario.
This method is run each time to create scenario context which is
actually just sub set of global context.
One of sample where this method is useful is users context.
We have set of users and tenants but each scenario should have access
to context of single user in single tenant.
"""
return context_obj
def __enter__(self):
return self
@@ -145,6 +177,26 @@ class ContextManager(object):
LOG.error("Context %s failed during cleanup." % ctx.get_name())
LOG.exception(e)
def map_for_scenario(self):
"""Returns scenario's specific context from full context.
Each context class is able to map context object data as it want.
E.g. users context will pick one user and it's tenant for each
scenario run.
This method iterates over all context classes used in task
and performs transformation of each of them to full context, order of
transformation is the same as order of context creation.
"""
# NOTE(boris-42): Original context_obj is read only and should not
# be modified
context_obj = copy.deepcopy(self.context_obj)
for ctx in self._get_sorted_context_lst():
context_obj = ctx.map_for_scenario(context_obj)
return context_obj
def __enter__(self):
try:
self.setup()

View File

@@ -16,16 +16,15 @@
import abc
import collections
import multiprocessing
import random
import time
import jsonschema
import six
from rally.common import log as logging
from rally.common.plugin import plugin
from rally.common import utils as rutils
from rally import consts
from rally.task import context
from rally.task.scenarios import base as scenario_base
from rally.task import types
from rally.task import utils
@@ -44,28 +43,18 @@ def format_result_on_timeout(exc, timeout):
}
def _get_scenario_context(context):
scenario_ctx = {}
for key, value in six.iteritems(context):
if key not in ["users", "tenants"]:
scenario_ctx[key] = value
if "users" in context:
user = random.choice(context["users"])
tenant = context["tenants"][user["tenant_id"]]
scenario_ctx["user"], scenario_ctx["tenant"] = user, tenant
return scenario_ctx
def _get_scenario_context(context_obj):
return context.ContextManager(context_obj).map_for_scenario()
def _run_scenario_once(args):
iteration, cls, method_name, context, kwargs = args
iteration, cls, method_name, context_obj, kwargs = args
LOG.info("Task %(task)s | ITER: %(iteration)s START" %
{"task": context["task"]["uuid"], "iteration": iteration})
{"task": context_obj["task"]["uuid"], "iteration": iteration})
context["iteration"] = iteration
scenario = cls(context=context)
context_obj["iteration"] = iteration
scenario = cls(context_obj)
error = []
scenario_output = {"errors": "", "data": {}}
@@ -80,7 +69,7 @@ def _run_scenario_once(args):
finally:
status = "Error %s: %s" % tuple(error[0:2]) if error else "OK"
LOG.info("Task %(task)s | ITER: %(iteration)s END: %(status)s" %
{"task": context["task"]["uuid"], "iteration": iteration,
{"task": context_obj["task"]["uuid"], "iteration": iteration,
"status": status})
return {"duration": timer.duration() - scenario.idle_duration(),

View File

@@ -1508,6 +1508,13 @@ class FakeContext(context.Context):
"additionalProperties": False
}
def __init__(self, context_obj=None):
context_obj = context_obj or {}
context_obj.setdefault("config", {})
context_obj["config"].setdefault("fake", None)
context_obj.setdefault("task", mock.MagicMock())
super(FakeContext, self).__init__(context_obj)
def setup(self):
pass
@@ -1535,9 +1542,7 @@ class FakeUserContext(FakeContext):
tenants = {"uuid": {"name": "tenant"}}
def __init__(self, ctx):
ctx.setdefault("task", mock.MagicMock())
super(FakeUserContext, self).__init__(ctx)
self.context.setdefault("admin", FakeUserContext.admin)
self.context.setdefault("users", [FakeUserContext.user])
self.context.setdefault("tenants", FakeUserContext.tenants)

View File

@@ -33,8 +33,7 @@ class ConstantScenarioRunnerTestCase(test.TestCase):
self.config = {"times": 4, "concurrency": 2,
"timeout": 2, "type": "constant",
"max_cpu_count": 2}
self.context = fakes.FakeUserContext({"task":
{"uuid": "uuid"}}).context
self.context = fakes.FakeContext({"task": {"uuid": "uuid"}}).context
self.args = {"a": 1}
self.task = mock.MagicMock()
@@ -235,8 +234,7 @@ class ConstantForDurationScenarioRunnerTestCase(test.TestCase):
super(ConstantForDurationScenarioRunnerTestCase, self).setUp()
self.config = {"duration": 0, "concurrency": 2,
"timeout": 2, "type": "constant_for_duration"}
self.context = fakes.FakeUserContext({"task":
{"uuid": "uuid"}}).context
self.context = fakes.FakeContext({"task": {"uuid": "uuid"}}).context
self.args = {"a": 1}
def test_validate(self):

View File

@@ -132,13 +132,11 @@ class RPSScenarioRunnerTestCase(test.TestCase):
@mock.patch(RUNNERS + "rps.time.sleep")
def test__run_scenario(self, mock_sleep):
context = fakes.FakeUserContext({}).context
context["task"] = {"uuid": "fake_uuid"}
config = {"times": 20, "rps": 20, "timeout": 5, "max_concurrency": 15}
runner_obj = rps.RPSScenarioRunner(self.task, config)
runner_obj._run_scenario(fakes.FakeScenario, "do_it", context, {})
runner_obj._run_scenario(fakes.FakeScenario, "do_it",
fakes.FakeContext({}).context, {})
self.assertEqual(len(runner_obj.result_queue), config["times"])
@@ -147,28 +145,23 @@ class RPSScenarioRunnerTestCase(test.TestCase):
@mock.patch(RUNNERS + "rps.time.sleep")
def test__run_scenario_exception(self, mock_sleep):
context = fakes.FakeUserContext({}).context
context["task"] = {"uuid": "fake_uuid"}
config = {"times": 4, "rps": 10}
runner_obj = rps.RPSScenarioRunner(self.task, config)
runner_obj._run_scenario(fakes.FakeScenario, "something_went_wrong",
context, {})
fakes.FakeContext({}).context, {})
self.assertEqual(len(runner_obj.result_queue), config["times"])
for result in runner_obj.result_queue:
self.assertIsNotNone(runner.ScenarioRunnerResult(result))
@mock.patch(RUNNERS + "rps.time.sleep")
def test__run_scenario_aborted(self, mock_sleep):
context = fakes.FakeUserContext({}).context
context["task"] = {"uuid": "fake_uuid"}
config = {"times": 20, "rps": 20, "timeout": 5}
runner_obj = rps.RPSScenarioRunner(self.task, config)
runner_obj.abort()
runner_obj._run_scenario(fakes.FakeScenario, "do_it", context, {})
runner_obj._run_scenario(fakes.FakeScenario, "do_it",
fakes.FakeUser().context, {})
self.assertEqual(len(runner_obj.result_queue), 0)
@@ -184,8 +177,6 @@ class RPSScenarioRunnerTestCase(test.TestCase):
def test_that_cpu_count_is_adjusted_properly(
self, mock__join_processes, mock__create_process_pool,
mock__log_debug_info, mock_cpu_count, mock_queue):
context = fakes.FakeUserContext({}).context
context["task"] = {"uuid": "fake_uuid"}
samples = [
{
@@ -260,7 +251,8 @@ class RPSScenarioRunnerTestCase(test.TestCase):
runner_obj = rps.RPSScenarioRunner(self.task, sample["input"])
runner_obj._run_scenario(fakes.FakeScenario, "do_it", context, {})
runner_obj._run_scenario(fakes.FakeScenario, "do_it",
fakes.FakeUser().context, {})
mock_cpu_count.assert_called_once_with()
mock__log_debug_info.assert_called_once_with(
@@ -283,9 +275,6 @@ class RPSScenarioRunnerTestCase(test.TestCase):
mock_queue.return_value)
def test_abort(self):
context = fakes.FakeUserContext({}).context
context["task"] = {"uuid": "fake_uuid"}
config = {"times": 4, "rps": 10}
runner_obj = rps.RPSScenarioRunner(self.task, config)

View File

@@ -22,9 +22,6 @@ from tests.unit import test
class SerialScenarioRunnerTestCase(test.TestCase):
def setUp(self):
super(SerialScenarioRunnerTestCase, self).setUp()
@mock.patch("rally.task.runner._run_scenario_once")
def test__run_scenario(self, mock__run_scenario_once):
times = 5
@@ -36,7 +33,7 @@ class SerialScenarioRunnerTestCase(test.TestCase):
runner = serial.SerialScenarioRunner(mock.MagicMock(),
{"times": times})
runner._run_scenario(fakes.FakeScenario, "do_it",
fakes.FakeUserContext({}).context, {})
fakes.FakeContext().context, {})
self.assertEqual(len(runner.result_queue), times)
results = list(runner.result_queue)
self.assertEqual(results, expected_results)
@@ -46,7 +43,7 @@ class SerialScenarioRunnerTestCase(test.TestCase):
{"times": 5})
runner.abort()
runner._run_scenario(fakes.FakeScenario, "do_it",
fakes.FakeUserContext({}).context, {})
fakes.FakeContext().context, {})
self.assertEqual(len(runner.result_queue), 0)
def test_abort(self):

View File

@@ -34,25 +34,6 @@ class ImageGeneratorTestCase(test.ScenarioTestCase):
tenants[str(id_)] = {"name": str(id_)}
return tenants
@mock.patch("%s.images.context.Context.__init__" % CTX)
def test_init(self, mock_context___init__):
context = {}
context["task"] = mock.MagicMock()
context["config"] = {
"images": {
"image_url": "mock_url",
"image_type": "qcow2",
"image_container": "bare",
"images_per_tenant": 4,
"image_name": "some_name",
"min_ram": 128,
"min_disk": 1,
}
}
images.ImageGenerator(context)
mock_context___init__.assert_called_once_with(context)
def test_init_validation(self):
context = {}
context["task"] = mock.MagicMock()

View File

@@ -24,6 +24,42 @@ from tests.unit import test
CTX = "rally.plugins.openstack.context.keystone.users"
class UserContextMixinTestCase(test.TestCase):
@mock.patch("%s.random.choice" % CTX, side_effect=lambda x: x[1])
def test_map_for_scenario(self, mock_choice):
users_ = []
tenants = {}
for i in range(2):
tenants[str(i)] = {"name": str(i)}
for j in range(3):
users_.append({"id": "%s_%s" % (i, j),
"tenant_id": str(i), "endpoint": "endpoint"})
context = {
"admin": mock.MagicMock(),
"users": users_,
"tenants": tenants,
"some_random_key": {
"nested": mock.MagicMock(),
"one_more": 10
}
}
chosen_tenant = context["tenants"][context["users"][1]["tenant_id"]]
expected_context = {
"admin": context["admin"],
"user": context["users"][1],
"tenant": chosen_tenant,
"some_random_key": context["some_random_key"]
}
self.assertEqual(
expected_context,
users.UserContextMixin().map_for_scenario(context)
)
class UserGeneratorTestCase(test.TestCase):
tenants_num = 1
@@ -42,6 +78,7 @@ class UserGeneratorTestCase(test.TestCase):
}
},
"admin": {"endpoint": mock.MagicMock()},
"users": [],
"task": {"uuid": "task_id"}
}
@@ -54,6 +91,10 @@ class UserGeneratorTestCase(test.TestCase):
self.osclients_patcher.stop()
super(UserGeneratorTestCase, self).tearDown()
def test_is_user_context_mixin_subclass(self):
self.assertTrue(
issubclass(users.UserGenerator, users.UserContextMixin))
@mock.patch("%s.network.wrap" % CTX)
def test__remove_default_security_group_not_needed(self, mock_wrap):
services = {"compute": consts.Service.NOVA}
@@ -148,8 +189,8 @@ class UserGeneratorTestCase(test.TestCase):
nova_admin.networks.list.return_value = networks
nova_admin.networks.get = fake_get_network
user_generator = users.UserGenerator(self.context)
user_generator.context["tenants"] = {"t1": dict(id="t1", name="t1"),
"t2": dict(id="t2", name="t2")}
user_generator.context["tenants"] = {"t1": {"id": "t1", "name": "t1"},
"t2": {"id": "t2", "name": "t2"}}
user_generator._remove_associated_networks()
mock_check_service_status.assert_called_once_with(mock.ANY,
"nova-network")
@@ -175,8 +216,8 @@ class UserGeneratorTestCase(test.TestCase):
nova_admin.networks.get = fake_get_network
nova_admin.networks.disassociate.side_effect = Exception()
user_generator = users.UserGenerator(self.context)
user_generator.context["tenants"] = {"t1": dict(id="t1", name="t1"),
"t2": dict(id="t2", name="t2")}
user_generator.context["tenants"] = {"t1": {"id": "t1", "name": "t1"},
"t2": {"id": "t2", "name": "t2"}}
user_generator._remove_associated_networks()
mock_check_service_status.assert_called_once_with(mock.ANY,
"nova-network")
@@ -196,8 +237,8 @@ class UserGeneratorTestCase(test.TestCase):
@mock.patch("%s.keystone" % CTX)
def test__create_users(self, mock_keystone, mock_sleep):
user_generator = users.UserGenerator(self.context)
user_generator.context["tenants"] = {"t1": dict(id="t1", name="t1"),
"t2": dict(id="t2", name="t2")}
user_generator.context["tenants"] = {"t1": {"id": "t1", "name": "t1"},
"t2": {"id": "t2", "name": "t2"}}
user_generator.config["users_per_tenant"] = 2
users_ = user_generator._create_users()
self.assertEqual(4, len(users_))
@@ -208,8 +249,8 @@ class UserGeneratorTestCase(test.TestCase):
@mock.patch("%s.keystone" % CTX)
def test__delete_tenants(self, mock_keystone):
user_generator = users.UserGenerator(self.context)
user_generator.context["tenants"] = {"t1": dict(id="t1", name="t1"),
"t2": dict(id="t2", name="t2")}
user_generator.context["tenants"] = {"t1": {"id": "t1", "name": "t1"},
"t2": {"id": "t2", "name": "t2"}}
user_generator._delete_tenants()
self.assertEqual(len(user_generator.context["tenants"]), 0)
@@ -218,8 +259,8 @@ class UserGeneratorTestCase(test.TestCase):
wrapped_keystone = mock_keystone.wrap.return_value
wrapped_keystone.delete_project.side_effect = Exception()
user_generator = users.UserGenerator(self.context)
user_generator.context["tenants"] = {"t1": dict(id="t1", name="t1"),
"t2": dict(id="t2", name="t2")}
user_generator.context["tenants"] = {"t1": {"id": "t1", "name": "t1"},
"t2": {"id": "t2", "name": "t2"}}
user_generator._delete_tenants()
self.assertEqual(len(user_generator.context["tenants"]), 0)

View File

@@ -41,10 +41,11 @@ class BaseContextTestCase(test.TestCase):
def test_init_empty_context(self):
ctx0 = {
"task": mock.MagicMock()
"task": mock.MagicMock(),
"config": {"fake": "test"}
}
ctx = fakes.FakeContext(ctx0)
self.assertEqual(ctx.config, {})
self.assertEqual(ctx.config, ctx0["config"]["fake"])
self.assertEqual(ctx.task, ctx0["task"])
self.assertEqual(ctx.context, ctx0)

View File

@@ -29,7 +29,7 @@ from tests.unit import test
BASE = "rally.task.runner."
class ScenarioHelpersTestCase(test.TestCase):
class ScenarioRunnerHelpersTestCase(test.TestCase):
@mock.patch(BASE + "utils.format_exc")
def test_format_result_on_timeout(self, mock_format_exc):
@@ -47,47 +47,27 @@ class ScenarioHelpersTestCase(test.TestCase):
expected)
mock_format_exc.assert_called_once_with(mock_exc)
@mock.patch(BASE + "random.choice", side_effect=lambda x: x[1])
def test_get_scenario_context(self, mock_choice):
@mock.patch(BASE + "context.ContextManager")
def test_get_scenario_context(self, mock_context_manager):
mock_context_obj = mock.MagicMock()
mock_map_for_scenario = (
mock_context_manager.return_value.map_for_scenario)
users = []
tenants = {}
self.assertEqual(mock_map_for_scenario.return_value,
runner._get_scenario_context(mock_context_obj))
for i in range(2):
tenants[str(i)] = dict(name=str(i))
for j in range(3):
users.append({"id": "%s_%s" % (i, j),
"tenant_id": str(i), "endpoint": "endpoint"})
context = {
"admin": mock.MagicMock(),
"users": users,
"tenants": tenants,
"some_random_key": {
"nested": mock.MagicMock(),
"one_more": 10
}
}
chosen_tenant = context["tenants"][context["users"][1]["tenant_id"]]
expected_context = {
"admin": context["admin"],
"user": context["users"][1],
"tenant": chosen_tenant,
"some_random_key": context["some_random_key"]
}
self.assertEqual(expected_context,
runner._get_scenario_context(context))
mock_context_manager.assert_called_once_with(mock_context_obj)
mock_map_for_scenario.assert_called_once_with()
def test_run_scenario_once_internal_logic(self):
context = runner._get_scenario_context(
fakes.FakeUserContext({}).context)
fakes.FakeContext({}).context)
scenario_cls = mock.MagicMock()
args = (2, scenario_cls, "test", context, {})
runner._run_scenario_once(args)
expected_calls = [
mock.call(context=context),
mock.call(context),
mock.call().test(),
mock.call().idle_duration(),
mock.call().idle_duration(),
@@ -97,9 +77,7 @@ class ScenarioHelpersTestCase(test.TestCase):
@mock.patch(BASE + "rutils.Timer", side_effect=fakes.FakeTimer)
def test_run_scenario_once_without_scenario_output(self, mock_timer):
context = runner._get_scenario_context(
fakes.FakeUserContext({}).context)
args = (1, fakes.FakeScenario, "do_it", context, {})
args = (1, fakes.FakeScenario, "do_it", mock.MagicMock(), {})
result = runner._run_scenario_once(args)
expected_result = {
@@ -114,9 +92,7 @@ class ScenarioHelpersTestCase(test.TestCase):
@mock.patch(BASE + "rutils.Timer", side_effect=fakes.FakeTimer)
def test_run_scenario_once_with_scenario_output(self, mock_timer):
context = runner._get_scenario_context(
fakes.FakeUserContext({}).context)
args = (1, fakes.FakeScenario, "with_output", context, {})
args = (1, fakes.FakeScenario, "with_output", mock.MagicMock(), {})
result = runner._run_scenario_once(args)
expected_result = {
@@ -131,9 +107,8 @@ class ScenarioHelpersTestCase(test.TestCase):
@mock.patch(BASE + "rutils.Timer", side_effect=fakes.FakeTimer)
def test_run_scenario_once_exception(self, mock_timer):
context = runner._get_scenario_context(
fakes.FakeUserContext({}).context)
args = (1, fakes.FakeScenario, "something_went_wrong", context, {})
args = (1, fakes.FakeScenario, "something_went_wrong",
mock.MagicMock(), {})
result = runner._run_scenario_once(args)
expected_error = result.pop("error")
expected_result = {