Refactor hook stuff
- Triggers should care about triggering(launching) hooks, not only about checking when hooks should be launched. This logic is moved from HookExecutor to Triggers - Remove "n/a" status of Hooks. There is no possible situation when such status can be configured. Also, "success" status is used now as default status. - `sys.call` saves output for success call too. - `get_configured_event_type` method of Trigger is renamed to `get_listening_event`. - Task Engine should not launch hook executor and timers if hooks section is not configured - HookExecutor doesn't create and start timer thread if "time" unit is not configured in any hook. - Results of Hooks relate to each particular trigger, so they are stored in trigger object and HookExecutor obtains results from there. Co-Authored-By: Andrey Kurilin <andr.kurilin@gmail.com> Change-Id: I2ce6132e101790af75ed0899bc4d5e3ddb00d0d7
This commit is contained in:
parent
228d8c246f
commit
ad52ccae24
@ -470,7 +470,7 @@ class TaskCommands(object):
|
||||
|
||||
results = [{"key": x["key"], "result": x["data"]["raw"],
|
||||
"sla": x["data"]["sla"],
|
||||
"hooks": x["data"]["hooks"],
|
||||
"hooks": x["data"].get("hooks", []),
|
||||
"load_duration": x["data"]["load_duration"],
|
||||
"full_duration": x["data"]["full_duration"]}
|
||||
for x in task.get_results()]
|
||||
@ -580,7 +580,7 @@ class TaskCommands(object):
|
||||
task_results = map(
|
||||
lambda x: {"key": x["key"],
|
||||
"sla": x["data"]["sla"],
|
||||
"hooks": x["data"]["hooks"],
|
||||
"hooks": x["data"].get("hooks", []),
|
||||
"result": x["data"]["raw"],
|
||||
"load_duration": x["data"]["load_duration"],
|
||||
"full_duration": x["data"]["full_duration"]},
|
||||
@ -660,7 +660,7 @@ class TaskCommands(object):
|
||||
tasks_results = map(
|
||||
lambda x: {"key": x["key"],
|
||||
"sla": x["data"]["sla"],
|
||||
"hooks": x["data"]["hooks"],
|
||||
"hooks": x["data"].get("hooks", []),
|
||||
"result": x["data"]["raw"],
|
||||
"load_duration": x["data"]["load_duration"],
|
||||
"full_duration": x["data"]["full_duration"]},
|
||||
|
@ -96,33 +96,18 @@ OUTPUT_SCHEMA = {
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
HOOK_RESULT_SCHEMA = {
|
||||
HOOK_RUN_RESULT_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"hook": {"type": "string"},
|
||||
"started_at": {"type": "number"},
|
||||
"finished_at": {"type": "number"},
|
||||
"triggered_by": {
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"iteration": {"type": "integer"},
|
||||
},
|
||||
"required": ["iteration"],
|
||||
"additionalProperties": False,
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"time": {"type": "integer"},
|
||||
},
|
||||
"required": ["time"],
|
||||
"additionalProperties": False,
|
||||
},
|
||||
]
|
||||
"properties": {"event_type": {"type": "string"},
|
||||
"value": {}},
|
||||
"required": ["event_type", "value"],
|
||||
"additionalProperties": False
|
||||
},
|
||||
"description": {"type": "string"},
|
||||
"status": {"type": "string"},
|
||||
"error": {
|
||||
"type": "array",
|
||||
@ -132,14 +117,19 @@ HOOK_RESULT_SCHEMA = {
|
||||
},
|
||||
"output": OUTPUT_SCHEMA,
|
||||
},
|
||||
"required": [
|
||||
"hook",
|
||||
"started_at",
|
||||
"finished_at",
|
||||
"triggered_by",
|
||||
"description",
|
||||
"status",
|
||||
],
|
||||
"required": ["finished_at", "triggered_by", "status"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
HOOK_RESULTS_SCHEMA = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config": {"type": "object"},
|
||||
"results": {"type": "array",
|
||||
"items": HOOK_RUN_RESULT_SCHEMA},
|
||||
"summary": {"type": "object"}
|
||||
},
|
||||
"required": ["config", "results", "summary"],
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
@ -179,10 +169,7 @@ TASK_RESULT_SCHEMA = {
|
||||
}
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
"type": "array",
|
||||
"items": HOOK_RESULT_SCHEMA,
|
||||
},
|
||||
"hooks": {"type": "array", "items": HOOK_RESULTS_SCHEMA},
|
||||
"result": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@ -228,8 +215,7 @@ TASK_RESULT_SCHEMA = {
|
||||
"type": "number",
|
||||
},
|
||||
},
|
||||
"required": ["key", "sla", "hooks", "result", "load_duration",
|
||||
"full_duration"],
|
||||
"required": ["key", "sla", "result", "load_duration", "full_duration"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
@ -270,10 +256,7 @@ TASK_EXTENDED_RESULT_SCHEMA = {
|
||||
}
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
"type": "array",
|
||||
"items": HOOK_RESULT_SCHEMA,
|
||||
},
|
||||
"hooks": {"type": "array", "items": HOOK_RESULTS_SCHEMA},
|
||||
"iterations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@ -327,7 +310,7 @@ TASK_EXTENDED_RESULT_SCHEMA = {
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["key", "sla", "hooks", "iterations", "info"],
|
||||
"required": ["key", "sla", "iterations", "info"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
@ -527,7 +510,7 @@ class Task(object):
|
||||
else:
|
||||
scenario["iterations"] = iter(iterations)
|
||||
scenario["sla"] = scenario["data"]["sla"]
|
||||
scenario["hooks"] = scenario["data"]["hooks"]
|
||||
scenario["hooks"] = scenario["data"].get("hooks", [])
|
||||
del scenario["data"]
|
||||
del scenario["task_uuid"]
|
||||
del scenario["id"]
|
||||
|
@ -183,7 +183,6 @@ class _ServiceType(utils.ImmutableMixin, utils.EnumMixin):
|
||||
|
||||
class _HookStatus(utils.ImmutableMixin, utils.EnumMixin):
|
||||
"""Hook result statuses."""
|
||||
UNKNOWN = "n/a"
|
||||
SUCCESS = "success"
|
||||
FAILED = "failed"
|
||||
VALIDATION_FAILED = "validation_failed"
|
||||
|
@ -69,7 +69,7 @@ class FileExporter(exporter.Exporter):
|
||||
|
||||
task_results = [{"key": x["key"], "result": x["data"]["raw"],
|
||||
"sla": x["data"]["sla"],
|
||||
"hooks": x["data"]["hooks"],
|
||||
"hooks": x["data"].get("hooks"),
|
||||
"load_duration": x["data"]["load_duration"],
|
||||
"full_duration": x["data"]["full_duration"]}
|
||||
for x in task.get_results()]
|
||||
|
@ -41,9 +41,8 @@ class SysCallHook(hook.Hook):
|
||||
proc.wait()
|
||||
LOG.debug("sys_call hook: Command %s returned %s",
|
||||
self.config, proc.returncode)
|
||||
|
||||
if proc.returncode == 0:
|
||||
self.set_status(consts.HookStatus.SUCCESS)
|
||||
self.set_output(proc.stdout.read().decode())
|
||||
else:
|
||||
self.set_error(
|
||||
exception_name="n/a", # no exception class
|
||||
|
@ -296,7 +296,7 @@ class ConstantForDurationScenarioRunner(runner.ScenarioRunner):
|
||||
def event_listener():
|
||||
while not stop_event_listener.isSet():
|
||||
while not event_queue.empty():
|
||||
self._send_event(event_queue.get())
|
||||
self.send_event(**event_queue.get())
|
||||
else:
|
||||
time.sleep(0.01)
|
||||
|
||||
|
@ -60,8 +60,12 @@ class EventTrigger(trigger.Trigger):
|
||||
]
|
||||
}
|
||||
|
||||
def get_configured_event_type(self):
|
||||
def get_listening_event(self):
|
||||
return self.config["unit"]
|
||||
|
||||
def is_runnable(self, value):
|
||||
return value in self.config["at"]
|
||||
def on_event(self, event_type, value=None):
|
||||
if not (event_type == self.get_listening_event()
|
||||
and value in self.config["at"]):
|
||||
# do nothing
|
||||
return
|
||||
super(EventTrigger, self).on_event(event_type, value)
|
||||
|
@ -71,16 +71,16 @@ class ResultConsumer(object):
|
||||
self.is_done = threading.Event()
|
||||
self.unexpected_failure = {}
|
||||
self.results = []
|
||||
self.thread = threading.Thread(
|
||||
target=self._consume_results
|
||||
)
|
||||
self.thread = threading.Thread(target=self._consume_results)
|
||||
self.aborting_checker = threading.Thread(target=self.wait_and_abort)
|
||||
self.event_thread = threading.Thread(target=self._consume_events)
|
||||
if "hooks" in self.key["kw"]:
|
||||
self.event_thread = threading.Thread(target=self._consume_events)
|
||||
|
||||
def __enter__(self):
|
||||
self.thread.start()
|
||||
self.aborting_checker.start()
|
||||
self.event_thread.start()
|
||||
if "hooks" in self.key["kw"]:
|
||||
self.event_thread.start()
|
||||
self.start = time.time()
|
||||
return self
|
||||
|
||||
@ -104,7 +104,7 @@ class ResultConsumer(object):
|
||||
time.sleep(0.1)
|
||||
|
||||
def _consume_events(self):
|
||||
while not self.is_done.isSet():
|
||||
while not self.is_done.isSet() or self.runner.event_queue:
|
||||
if self.runner.event_queue:
|
||||
event = self.runner.event_queue.popleft()
|
||||
self.hook_executor.on_event(
|
||||
@ -117,7 +117,6 @@ class ResultConsumer(object):
|
||||
self.is_done.set()
|
||||
self.aborting_checker.join()
|
||||
self.thread.join()
|
||||
self.event_thread.join()
|
||||
|
||||
if exc_type:
|
||||
self.sla_checker.set_unexpected_failure(exc_value)
|
||||
@ -138,13 +137,18 @@ class ResultConsumer(object):
|
||||
LOG.info("Full duration is %s" % utils.format_float_to_str(
|
||||
self.finish - self.start))
|
||||
|
||||
self.task.append_results(self.key, {
|
||||
results = {
|
||||
"raw": self.results,
|
||||
"load_duration": load_duration,
|
||||
"full_duration": self.finish - self.start,
|
||||
"sla": self.sla_checker.results(),
|
||||
"hooks": self.hook_executor.results(),
|
||||
})
|
||||
}
|
||||
self.runner.send_event(type="load_finished", value=results)
|
||||
if "hooks" in self.key["kw"]:
|
||||
self.event_thread.join()
|
||||
results["hooks"] = self.hook_executor.results()
|
||||
|
||||
self.task.append_results(self.key, results)
|
||||
|
||||
@staticmethod
|
||||
def is_task_in_aborting_status(task_uuid, check_soft=True):
|
||||
|
@ -22,7 +22,6 @@ import six
|
||||
|
||||
from rally.common.i18n import _, _LE
|
||||
from rally.common import logging
|
||||
from rally.common import objects
|
||||
from rally.common.plugin import plugin
|
||||
from rally.common import utils as rutils
|
||||
from rally import consts
|
||||
@ -42,22 +41,18 @@ class HookExecutor(object):
|
||||
def __init__(self, config, task):
|
||||
self.config = config
|
||||
self.task = task
|
||||
self._timer_thread = threading.Thread(target=self._timer_method)
|
||||
self._timer_stop_event = threading.Event()
|
||||
|
||||
# map triggers to event types
|
||||
self.triggers = collections.defaultdict(list)
|
||||
for hook in config.get("hooks", []):
|
||||
hook_cls = Hook.get(hook["name"])
|
||||
trigger_obj = trigger.Trigger.get(
|
||||
hook["trigger"]["name"])(hook["trigger"]["args"])
|
||||
trigger_event_type = trigger_obj.get_configured_event_type()
|
||||
self.triggers[trigger_event_type].append(
|
||||
(trigger_obj, hook["name"], hook["args"],
|
||||
hook.get("description", "n/a"))
|
||||
)
|
||||
hook["trigger"]["name"])(hook, self.task, hook_cls)
|
||||
event_type = trigger_obj.get_listening_event()
|
||||
self.triggers[event_type].append(trigger_obj)
|
||||
|
||||
# list of executed hooks
|
||||
self.hooks = []
|
||||
if "time" in self.triggers:
|
||||
self._timer_thread = threading.Thread(target=self._timer_method)
|
||||
self._timer_stop_event = threading.Event()
|
||||
|
||||
def _timer_method(self):
|
||||
"""Timer thread method.
|
||||
@ -88,26 +83,27 @@ class HookExecutor(object):
|
||||
particular event occurred.
|
||||
It runs hooks configured for event.
|
||||
"""
|
||||
# start timer on first iteration
|
||||
if self.triggers["time"]:
|
||||
if "time" in self.triggers:
|
||||
# start timer on first iteration
|
||||
if event_type == "iteration" and value == 1:
|
||||
self._start_timer()
|
||||
|
||||
triggers = self.triggers[event_type]
|
||||
for trigger_obj, hook_name, hook_args, hook_description in triggers:
|
||||
if trigger_obj.is_runnable(value=value):
|
||||
hook = Hook.get(hook_name)(task=self.task, config=hook_args,
|
||||
triggered_by={event_type: value},
|
||||
description=hook_description)
|
||||
self.hooks.append(hook)
|
||||
hook.run_async()
|
||||
for trigger_obj in self.triggers[event_type]:
|
||||
started = trigger_obj.on_event(event_type, value)
|
||||
if started:
|
||||
LOG.info(_("Hook %s is trigged for Task %s by %s=%s")
|
||||
% (hook_name, self.task["uuid"], event_type, value))
|
||||
% (trigger_obj.hook_cls.__name__, self.task["uuid"],
|
||||
event_type, value))
|
||||
|
||||
def results(self):
|
||||
"""Returns list of dicts with hook results."""
|
||||
self._stop_timer()
|
||||
return [hook.result() for hook in self.hooks]
|
||||
if "time" in self.triggers:
|
||||
self._stop_timer()
|
||||
results = []
|
||||
for triggers_group in self.triggers.values():
|
||||
for trigger_obj in triggers_group:
|
||||
results.append(trigger_obj.get_results())
|
||||
return results
|
||||
|
||||
|
||||
@plugin.base()
|
||||
@ -115,55 +111,31 @@ class HookExecutor(object):
|
||||
class Hook(plugin.Plugin):
|
||||
"""Factory for hook classes."""
|
||||
|
||||
@classmethod
|
||||
def validate(cls, config):
|
||||
hook_schema = cls.get(config["name"]).CONFIG_SCHEMA
|
||||
jsonschema.validate(config["args"], hook_schema)
|
||||
CONFIG_SCHEMA = {}
|
||||
|
||||
trigger.Trigger.validate(config["trigger"])
|
||||
|
||||
def __init__(self, task, config, triggered_by, description):
|
||||
def __init__(self, task, config, triggered_by):
|
||||
self.task = task
|
||||
self.config = config
|
||||
self._triggered_by = triggered_by
|
||||
self._description = description
|
||||
self._thread = threading.Thread(target=self._thread_method)
|
||||
self._started_at = 0.0
|
||||
self._finished_at = 0.0
|
||||
self._result = self._format_result(status=consts.HookStatus.UNKNOWN)
|
||||
|
||||
def _format_result(self, status, error=None):
|
||||
"""Returns hook result dict."""
|
||||
result = {
|
||||
"hook": self.get_name(),
|
||||
"status": status,
|
||||
"description": self._description,
|
||||
self._result = {
|
||||
"status": consts.HookStatus.SUCCESS,
|
||||
"started_at": self._started_at,
|
||||
"finished_at": self._finished_at,
|
||||
"triggered_by": self._triggered_by,
|
||||
}
|
||||
if error is not None:
|
||||
result["error"] = error
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def validate(cls, config):
|
||||
jsonschema.validate(config["args"], cls.CONFIG_SCHEMA)
|
||||
|
||||
trigger.Trigger.validate(config["trigger"])
|
||||
|
||||
def _thread_method(self):
|
||||
# Run hook synchronously
|
||||
self.run_sync()
|
||||
self._validate_result_schema()
|
||||
|
||||
def _validate_result_schema(self):
|
||||
"""Validates result format."""
|
||||
try:
|
||||
jsonschema.validate(self._result, objects.task.HOOK_RESULT_SCHEMA)
|
||||
except jsonschema.ValidationError as validation_error:
|
||||
LOG.error(_LE("Hook %s returned result "
|
||||
"in wrong format.") % self.get_name())
|
||||
LOG.exception(validation_error)
|
||||
|
||||
self._result = self._format_result(
|
||||
status=consts.HookStatus.VALIDATION_FAILED,
|
||||
error=utils.format_exc(validation_error),
|
||||
)
|
||||
|
||||
def set_error(self, exception_name, description, details):
|
||||
"""Set error related information to result.
|
||||
@ -173,7 +145,8 @@ class Hook(plugin.Plugin):
|
||||
:param details: any details as string
|
||||
"""
|
||||
self.set_status(consts.HookStatus.FAILED)
|
||||
self._result["error"] = [exception_name, description, details]
|
||||
self._result["error"] = {"etype": exception_name,
|
||||
"msg": description, "details": details}
|
||||
|
||||
def set_status(self, status):
|
||||
"""Set status to result."""
|
||||
@ -184,7 +157,8 @@ class Hook(plugin.Plugin):
|
||||
|
||||
:param output: Diagram data in task.OUTPUT_SCHEMA format
|
||||
"""
|
||||
self._result["output"] = output
|
||||
if output:
|
||||
self._result["output"] = output
|
||||
|
||||
def run_async(self):
|
||||
"""Run hook asynchronously."""
|
||||
|
@ -137,7 +137,7 @@ def _extend_results(results):
|
||||
"task_uuid": None,
|
||||
"key": result["key"],
|
||||
"data": {"sla": result["sla"],
|
||||
"hooks": result["hooks"],
|
||||
"hooks": result.get("hooks"),
|
||||
"raw": result["result"],
|
||||
"full_duration": result["full_duration"],
|
||||
"load_duration": result["load_duration"]},
|
||||
|
@ -217,7 +217,7 @@ class ScenarioRunner(plugin.Plugin):
|
||||
time.sleep(0.01)
|
||||
|
||||
while not event_queue.empty():
|
||||
self._send_event(event_queue.get())
|
||||
self.send_event(**event_queue.get())
|
||||
|
||||
while not result_queue.empty():
|
||||
self._send_result(result_queue.get())
|
||||
@ -323,12 +323,14 @@ class ScenarioRunner(plugin.Plugin):
|
||||
self.result_queue.append(sorted_batch)
|
||||
del self.result_batch[:]
|
||||
|
||||
def _send_event(self, event):
|
||||
def send_event(self, type, value=None):
|
||||
"""Store event to send it to consumer later.
|
||||
|
||||
:param event: Event dict to be sent.
|
||||
:param type: Event type
|
||||
:param value: Optional event data
|
||||
"""
|
||||
self.event_queue.append(event)
|
||||
self.event_queue.append({"type": type,
|
||||
"value": value})
|
||||
|
||||
def _log_debug_info(self, **info):
|
||||
"""Log runner parameters for debugging.
|
||||
|
@ -18,28 +18,54 @@ import abc
|
||||
import jsonschema
|
||||
import six
|
||||
|
||||
from rally.common.i18n import _
|
||||
from rally.common import logging
|
||||
from rally.common.plugin import plugin
|
||||
|
||||
configure = plugin.configure
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@plugin.base()
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Trigger(plugin.Plugin):
|
||||
"""Factory for trigger classes."""
|
||||
|
||||
CONFIG_SCHEMA = {}
|
||||
|
||||
def __init__(self, context, task, hook_cls):
|
||||
self.context = context
|
||||
self.config = self.context["trigger"]["args"]
|
||||
self.task = task
|
||||
self.hook_cls = hook_cls
|
||||
self._runs = []
|
||||
|
||||
@classmethod
|
||||
def validate(cls, config):
|
||||
trigger_schema = cls.get(config["name"]).CONFIG_SCHEMA
|
||||
jsonschema.validate(config["args"], trigger_schema)
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
jsonschema.validate(config["args"], cls.CONFIG_SCHEMA)
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_configured_event_type(self):
|
||||
"""Returns supported event type."""
|
||||
def get_listening_event(self):
|
||||
"""Returns event type to listen."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_runnable(self, value):
|
||||
"""Returns True if trigger is active on specified event value."""
|
||||
def on_event(self, event_type, value=None):
|
||||
"""Launch hook on specified event."""
|
||||
LOG.info(_("Hook %s is trigged for Task %s by %s=%s")
|
||||
% (self.hook_cls.__name__, self.task["uuid"],
|
||||
event_type, value))
|
||||
hook = self.hook_cls(self.task, self.context.get("args", {}),
|
||||
{"event_type": event_type, "value": value})
|
||||
hook.run_async()
|
||||
self._runs.append(hook)
|
||||
|
||||
def get_results(self):
|
||||
results = {"config": self.context,
|
||||
"results": [],
|
||||
"summary": {}}
|
||||
for hook in self._runs:
|
||||
hook_result = hook.result()
|
||||
results["results"].append(hook_result)
|
||||
results["summary"].setdefault(hook_result["status"], 0)
|
||||
results["summary"][hook_result["status"]] += 1
|
||||
return results
|
||||
|
@ -1036,14 +1036,15 @@ class HookTestCase(unittest.TestCase):
|
||||
self.started = time.time()
|
||||
|
||||
def _assert_results_time(self, results):
|
||||
for result in results:
|
||||
started_at = result["started_at"]
|
||||
finished_at = result["finished_at"]
|
||||
self.assertIsInstance(started_at, float)
|
||||
self.assertGreater(started_at, self.started)
|
||||
self.assertIsInstance(finished_at, float)
|
||||
self.assertGreater(finished_at, self.started)
|
||||
self.assertGreater(finished_at, started_at)
|
||||
for trigger_results in results:
|
||||
for result in trigger_results["results"]:
|
||||
started_at = result["started_at"]
|
||||
finished_at = result["finished_at"]
|
||||
self.assertIsInstance(started_at, float)
|
||||
self.assertGreater(started_at, self.started)
|
||||
self.assertIsInstance(finished_at, float)
|
||||
self.assertGreater(finished_at, self.started)
|
||||
self.assertGreater(finished_at, started_at)
|
||||
|
||||
def _get_sample_task_config(self, cmd, description, runner):
|
||||
return {
|
||||
@ -1071,20 +1072,23 @@ class HookTestCase(unittest.TestCase):
|
||||
]
|
||||
}
|
||||
|
||||
def _get_result(self, description, iteration=None, second=None):
|
||||
triggered_by = {}
|
||||
if iteration is not None:
|
||||
triggered_by["iteration"] = iteration
|
||||
elif second is not None:
|
||||
triggered_by["time"] = second
|
||||
def _get_result(self, config, iterations=None, seconds=None):
|
||||
result = {
|
||||
"hook": "sys_call",
|
||||
"description": description,
|
||||
"finished_at": mock.ANY,
|
||||
"started_at": mock.ANY,
|
||||
"triggered_by": triggered_by,
|
||||
"status": "success",
|
||||
"config": config,
|
||||
"results": [],
|
||||
"summary": {"success": 0}
|
||||
}
|
||||
events = iterations if iterations else seconds
|
||||
event_type = "iteration" if iterations else "time"
|
||||
|
||||
for i in range(len(events)):
|
||||
result["results"].append({
|
||||
"finished_at": mock.ANY,
|
||||
"started_at": mock.ANY,
|
||||
"triggered_by": {"event_type": event_type, "value": events[i]},
|
||||
"status": "success"})
|
||||
result["summary"]["success"] += 1
|
||||
|
||||
return result
|
||||
|
||||
def test_hook_result_with_constant_runner(self):
|
||||
@ -1097,9 +1101,8 @@ class HookTestCase(unittest.TestCase):
|
||||
rally("task start --task %s" % config.filename)
|
||||
results = json.loads(rally("task results"))
|
||||
hook_results = results[0]["hooks"]
|
||||
expected = [
|
||||
self._get_result("event_hook", iteration=5)
|
||||
]
|
||||
hooks_cfg = cfg["Dummy.dummy"][0]["hooks"]
|
||||
expected = [self._get_result(hooks_cfg[0], iterations=[5])]
|
||||
self.assertEqual(expected, hook_results)
|
||||
self._assert_results_time(hook_results)
|
||||
|
||||
@ -1114,9 +1117,8 @@ class HookTestCase(unittest.TestCase):
|
||||
rally("task start --task %s" % config.filename)
|
||||
results = json.loads(rally("task results"))
|
||||
hook_results = results[0]["hooks"]
|
||||
expected = [
|
||||
self._get_result("event_hook", iteration=5)
|
||||
]
|
||||
hooks_cfg = cfg["Dummy.dummy"][0]["hooks"]
|
||||
expected = [self._get_result(hooks_cfg[0], iterations=[5])]
|
||||
self.assertEqual(expected, hook_results)
|
||||
self._assert_results_time(hook_results)
|
||||
|
||||
@ -1130,9 +1132,8 @@ class HookTestCase(unittest.TestCase):
|
||||
rally("task start --task %s" % config.filename)
|
||||
results = json.loads(rally("task results"))
|
||||
hook_results = results[0]["hooks"]
|
||||
expected = [
|
||||
self._get_result("event_hook", iteration=5)
|
||||
]
|
||||
hooks_cfg = cfg["Dummy.dummy"][0]["hooks"]
|
||||
expected = [self._get_result(hooks_cfg[0], iterations=[5])]
|
||||
self.assertEqual(expected, hook_results)
|
||||
self._assert_results_time(hook_results)
|
||||
|
||||
@ -1146,9 +1147,8 @@ class HookTestCase(unittest.TestCase):
|
||||
rally("task start --task %s" % config.filename)
|
||||
results = json.loads(rally("task results"))
|
||||
hook_results = results[0]["hooks"]
|
||||
expected = [
|
||||
self._get_result("event_hook", iteration=5)
|
||||
]
|
||||
hooks_cfg = cfg["Dummy.dummy"][0]["hooks"]
|
||||
expected = [self._get_result(hooks_cfg[0], iterations=[5])]
|
||||
self.assertEqual(expected, hook_results)
|
||||
self._assert_results_time(hook_results)
|
||||
|
||||
@ -1162,17 +1162,13 @@ class HookTestCase(unittest.TestCase):
|
||||
rally("task start --task %s" % config.filename)
|
||||
results = json.loads(rally("task results"))
|
||||
hook_results = results[0]["hooks"]
|
||||
expected = [
|
||||
{
|
||||
"description": "event_hook",
|
||||
"finished_at": mock.ANY,
|
||||
"started_at": mock.ANY,
|
||||
"hook": "sys_call",
|
||||
"triggered_by": {"iteration": 5},
|
||||
"status": "failed",
|
||||
"error": ["n/a", "Subprocess returned 1", ""],
|
||||
}
|
||||
]
|
||||
hooks_cfg = cfg["Dummy.dummy"][0]["hooks"]
|
||||
expected = [self._get_result(hooks_cfg[0], iterations=[5])]
|
||||
expected[0]["results"][0]["status"] = "failed"
|
||||
expected[0]["summary"] = {"failed": 1}
|
||||
expected[0]["results"][0]["error"] = {"etype": "n/a",
|
||||
"msg": "Subprocess returned 1",
|
||||
"details": ""}
|
||||
self.assertEqual(expected, hook_results)
|
||||
self._assert_results_time(hook_results)
|
||||
|
||||
@ -1200,11 +1196,9 @@ class HookTestCase(unittest.TestCase):
|
||||
rally("task start --task %s" % config.filename)
|
||||
results = json.loads(rally("task results"))
|
||||
hook_results = results[0]["hooks"]
|
||||
expected = [
|
||||
self._get_result("event_hook", iteration=5),
|
||||
self._get_result("time_hook", second=3),
|
||||
self._get_result("time_hook", second=6),
|
||||
self._get_result("time_hook", second=9),
|
||||
]
|
||||
|
||||
hooks_cfg = cfg["Dummy.dummy"][0]["hooks"]
|
||||
expected = [self._get_result(hooks_cfg[0], iterations=[5]),
|
||||
self._get_result(hooks_cfg[1], seconds=[3, 6, 9])]
|
||||
self.assertEqual(expected, hook_results)
|
||||
self._assert_results_time(hook_results)
|
||||
|
@ -20,7 +20,6 @@ import mock
|
||||
|
||||
from rally import consts
|
||||
from rally.plugins.common.hook import sys_call
|
||||
from rally.task import hook
|
||||
from tests.unit import fakes
|
||||
from tests.unit import test
|
||||
|
||||
@ -28,7 +27,7 @@ from tests.unit import test
|
||||
class SysCallHookTestCase(test.TestCase):
|
||||
|
||||
def test_validate(self):
|
||||
hook.Hook.validate(
|
||||
sys_call.SysCallHook.validate(
|
||||
{
|
||||
"name": "sys_call",
|
||||
"description": "list folder",
|
||||
@ -59,29 +58,27 @@ class SysCallHookTestCase(test.TestCase):
|
||||
}
|
||||
}
|
||||
self.assertRaises(
|
||||
jsonschema.ValidationError, hook.Hook.validate, conf)
|
||||
jsonschema.ValidationError, sys_call.SysCallHook.validate, conf)
|
||||
|
||||
@mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer)
|
||||
@mock.patch("subprocess.Popen")
|
||||
@mock.patch("rally.plugins.common.hook.sys_call.subprocess.Popen")
|
||||
def test_run(self, mock_popen, mock_timer):
|
||||
popen_instance = mock_popen.return_value
|
||||
popen_instance.returncode = 0
|
||||
|
||||
task = mock.MagicMock()
|
||||
sys_call_hook = sys_call.SysCallHook(task, "/bin/bash -c 'ls'",
|
||||
{"iteration": 1}, "dummy_action")
|
||||
{"iteration": 1})
|
||||
|
||||
sys_call_hook.run_sync()
|
||||
sys_call_hook._validate_result_schema()
|
||||
|
||||
self.assertEqual(
|
||||
{
|
||||
"hook": "sys_call",
|
||||
"description": "dummy_action",
|
||||
"triggered_by": {"iteration": 1},
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"status": consts.HookStatus.SUCCESS,
|
||||
"output": mock_popen.return_value.stdout.read().decode()
|
||||
}, sys_call_hook.result())
|
||||
|
||||
mock_popen.assert_called_once_with(
|
||||
@ -90,7 +87,7 @@ class SysCallHookTestCase(test.TestCase):
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
@mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer)
|
||||
@mock.patch("subprocess.Popen")
|
||||
@mock.patch("rally.plugins.common.hook.sys_call.subprocess.Popen")
|
||||
def test_run_error(self, mock_popen, mock_timer):
|
||||
popen_instance = mock_popen.return_value
|
||||
popen_instance.returncode = 1
|
||||
@ -98,24 +95,21 @@ class SysCallHookTestCase(test.TestCase):
|
||||
|
||||
task = mock.MagicMock()
|
||||
sys_call_hook = sys_call.SysCallHook(task, "/bin/bash -c 'ls'",
|
||||
{"iteration": 1}, "dummy_action")
|
||||
{"iteration": 1})
|
||||
|
||||
sys_call_hook.run_sync()
|
||||
sys_call_hook._validate_result_schema()
|
||||
|
||||
self.assertEqual(
|
||||
{
|
||||
"hook": "sys_call",
|
||||
"description": "dummy_action",
|
||||
"triggered_by": {"iteration": 1},
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"status": consts.HookStatus.FAILED,
|
||||
"error": [
|
||||
"n/a",
|
||||
"Subprocess returned 1",
|
||||
"No such file or directory",
|
||||
]
|
||||
"error": {
|
||||
"etype": "n/a",
|
||||
"msg": "Subprocess returned 1",
|
||||
"details": "No such file or directory",
|
||||
}
|
||||
}, sys_call_hook.result())
|
||||
|
||||
mock_popen.assert_called_once_with(
|
||||
|
@ -15,8 +15,9 @@
|
||||
|
||||
import ddt
|
||||
import jsonschema
|
||||
import mock
|
||||
|
||||
from rally.task import trigger
|
||||
from rally.plugins.common.trigger import event
|
||||
from tests.unit import test
|
||||
|
||||
|
||||
@ -29,8 +30,10 @@ class EventTriggerTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(EventTriggerTestCase, self).setUp()
|
||||
self.trigger = trigger.Trigger.get("event")({"unit": "iteration",
|
||||
"at": [1, 4, 5]})
|
||||
self.hook_cls = mock.MagicMock(__name__="name")
|
||||
self.trigger = event.EventTrigger({"trigger": {"args": {
|
||||
"unit": "iteration", "at": [1, 4, 5]}}},
|
||||
mock.MagicMock(), self.hook_cls)
|
||||
|
||||
@ddt.data((create_config(unit="time", at=[0, 3, 5]), True),
|
||||
(create_config(unit="time", at=[2, 2]), False),
|
||||
@ -52,18 +55,18 @@ class EventTriggerTestCase(test.TestCase):
|
||||
@ddt.unpack
|
||||
def test_config_schema(self, config, valid):
|
||||
if valid:
|
||||
trigger.Trigger.validate(config)
|
||||
event.EventTrigger.validate(config)
|
||||
else:
|
||||
self.assertRaises(jsonschema.ValidationError,
|
||||
trigger.Trigger.validate, config)
|
||||
event.EventTrigger.validate, config)
|
||||
|
||||
def test_get_configured_event_type(self):
|
||||
event_type = self.trigger.get_configured_event_type()
|
||||
def test_get_listening_event(self):
|
||||
event_type = self.trigger.get_listening_event()
|
||||
self.assertEqual("iteration", event_type)
|
||||
|
||||
@ddt.data((1, True), (4, True), (5, True),
|
||||
(0, False), (2, False), (3, False), (6, False), (7, False))
|
||||
@ddt.unpack
|
||||
def test_is_runnable(self, value, expected_result):
|
||||
result = self.trigger.is_runnable(value)
|
||||
self.assertIs(result, expected_result)
|
||||
def test_on_event(self, value, should_call):
|
||||
self.trigger.on_event("iteration", value)
|
||||
self.assertEqual(should_call, self.hook_cls.called)
|
||||
|
@ -533,8 +533,6 @@ class ResultConsumerTestCase(test.TestCase):
|
||||
mock_sla_results = mock.MagicMock()
|
||||
mock_sla_checker.return_value = mock_sla_instance
|
||||
mock_sla_instance.results.return_value = mock_sla_results
|
||||
mock_hook_executor_instance = mock_hook_executor.return_value
|
||||
mock_hook_results = mock_hook_executor_instance.results.return_value
|
||||
mock_task_get_status.return_value = consts.TaskStatus.RUNNING
|
||||
key = {"kw": {"fake": 2}, "name": "fake", "pos": 0}
|
||||
task = mock.MagicMock()
|
||||
@ -551,7 +549,6 @@ class ResultConsumerTestCase(test.TestCase):
|
||||
"raw": [],
|
||||
"full_duration": 1,
|
||||
"sla": mock_sla_results,
|
||||
"hooks": mock_hook_results,
|
||||
"load_duration": 0
|
||||
}
|
||||
)], any_order=True)
|
||||
@ -674,7 +671,7 @@ class ResultConsumerTestCase(test.TestCase):
|
||||
mock_hook_results = mock_hook_executor_instance.results.return_value
|
||||
|
||||
mock_task_get_status.return_value = consts.TaskStatus.RUNNING
|
||||
key = {"kw": {"fake": 2}, "name": "fake", "pos": 0}
|
||||
key = {"kw": {"fake": 2, "hooks": []}, "name": "fake", "pos": 0}
|
||||
task = mock.MagicMock()
|
||||
runner = mock.MagicMock()
|
||||
events = [
|
||||
|
@ -80,12 +80,14 @@ class HookExecutorTestCase(test.TestCase):
|
||||
hook_executor.on_event(event_type="iteration", value=1)
|
||||
|
||||
self.assertEqual(
|
||||
[{"description": "dummy_action",
|
||||
"hook": "dummy_hook",
|
||||
"triggered_by": {"iteration": 1},
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"status": consts.HookStatus.SUCCESS}], hook_executor.results())
|
||||
[{"config": self.conf["hooks"][0],
|
||||
"results": [{
|
||||
"triggered_by": {"event_type": "iteration", "value": 1},
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"status": consts.HookStatus.SUCCESS}],
|
||||
"summary": {consts.HookStatus.SUCCESS: 1}}],
|
||||
hook_executor.results())
|
||||
|
||||
@mock.patch("rally.task.hook.HookExecutor._timer_method")
|
||||
@mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer)
|
||||
@ -98,18 +100,23 @@ class HookExecutorTestCase(test.TestCase):
|
||||
hook_executor.on_event(event_type="iteration", value=1)
|
||||
|
||||
self.assertEqual(
|
||||
[{"description": "dummy_action",
|
||||
"hook": "dummy_hook",
|
||||
"triggered_by": {"iteration": 1},
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"error": ["Exception", "Description", "Traceback"],
|
||||
"output": {"additive": [], "complete": []},
|
||||
"status": consts.HookStatus.FAILED}], hook_executor.results())
|
||||
[{"config": self.conf["hooks"][0],
|
||||
"results": [{
|
||||
"triggered_by": {"event_type": "iteration", "value": 1},
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"error": {"details": "Traceback", "etype": "Exception",
|
||||
"msg": "Description"},
|
||||
"output": {"additive": [], "complete": []},
|
||||
"status": consts.HookStatus.FAILED}],
|
||||
"summary": {consts.HookStatus.FAILED: 1}}],
|
||||
hook_executor.results())
|
||||
|
||||
def test_empty_result(self):
|
||||
hook_executor = hook.HookExecutor(self.conf, self.task)
|
||||
self.assertEqual([], hook_executor.results())
|
||||
self.assertEqual([{"config": self.conf["hooks"][0], "results": [],
|
||||
"summary": {}}],
|
||||
hook_executor.results())
|
||||
|
||||
@mock.patch("rally.task.hook.HookExecutor._timer_method")
|
||||
@mock.patch.object(DummyHook, "run", side_effect=Exception("My err msg"))
|
||||
@ -120,30 +127,15 @@ class HookExecutorTestCase(test.TestCase):
|
||||
hook_executor.on_event(event_type="iteration", value=1)
|
||||
|
||||
self.assertEqual(
|
||||
[{"description": "dummy_action",
|
||||
"hook": "dummy_hook",
|
||||
"triggered_by": {"iteration": 1},
|
||||
"error": ["Exception", "My err msg", mock.ANY],
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"status": consts.HookStatus.FAILED}], hook_executor.results())
|
||||
|
||||
@mock.patch("rally.task.hook.HookExecutor._timer_method")
|
||||
@mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer)
|
||||
def test_result_wrong_format(self, mock_timer, mock__timer_method):
|
||||
hook_args = self.conf["hooks"][0]["args"]
|
||||
hook_args["status"] = 10
|
||||
hook_executor = hook.HookExecutor(self.conf, self.task)
|
||||
hook_executor.on_event(event_type="iteration", value=1)
|
||||
|
||||
self.assertEqual(
|
||||
[{"description": "dummy_action",
|
||||
"hook": "dummy_hook",
|
||||
"triggered_by": {"iteration": 1},
|
||||
"error": ["ValidationError", mock.ANY, mock.ANY],
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"status": consts.HookStatus.VALIDATION_FAILED}],
|
||||
[{"config": self.conf["hooks"][0],
|
||||
"results": [{
|
||||
"triggered_by": {"event_type": "iteration", "value": 1},
|
||||
"error": {"etype": "Exception",
|
||||
"msg": mock.ANY, "details": mock.ANY},
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"status": consts.HookStatus.FAILED}],
|
||||
"summary": {consts.HookStatus.FAILED: 1}}],
|
||||
hook_executor.results())
|
||||
|
||||
@mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer)
|
||||
@ -155,12 +147,14 @@ class HookExecutorTestCase(test.TestCase):
|
||||
hook_executor.on_event(event_type="time", value=1)
|
||||
|
||||
self.assertEqual(
|
||||
[{"description": "dummy_action",
|
||||
"hook": "dummy_hook",
|
||||
"triggered_by": {"time": 1},
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"status": consts.HookStatus.SUCCESS}], hook_executor.results())
|
||||
[{"config": self.conf["hooks"][0],
|
||||
"results": [{
|
||||
"triggered_by": {"event_type": "time", "value": 1},
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"status": consts.HookStatus.SUCCESS}],
|
||||
"summary": {consts.HookStatus.SUCCESS: 1}}],
|
||||
hook_executor.results())
|
||||
|
||||
@mock.patch("rally.common.utils.Stopwatch", autospec=True)
|
||||
@mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer)
|
||||
@ -180,12 +174,15 @@ class HookExecutorTestCase(test.TestCase):
|
||||
self.assertTrue(hook_executor._timer_stop_event.wait(1))
|
||||
|
||||
self.assertEqual(
|
||||
[{"description": "dummy_action",
|
||||
"hook": "dummy_hook",
|
||||
"triggered_by": {"time": 1},
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"status": consts.HookStatus.SUCCESS}], hook_executor.results())
|
||||
[{"config": self.conf["hooks"][0],
|
||||
"results": [{
|
||||
"triggered_by": {"event_type": "time", "value": 1},
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"status": consts.HookStatus.SUCCESS}],
|
||||
"summary": {consts.HookStatus.SUCCESS: 1}
|
||||
}],
|
||||
hook_executor.results())
|
||||
|
||||
stopwatch_inst.start.assert_called_once_with()
|
||||
stopwatch_inst.sleep.assert_has_calls([
|
||||
@ -198,7 +195,7 @@ class HookExecutorTestCase(test.TestCase):
|
||||
class HookTestCase(test.TestCase):
|
||||
|
||||
def test_validate(self):
|
||||
hook.Hook.validate(
|
||||
DummyHook.validate(
|
||||
{
|
||||
"name": "dummy_hook",
|
||||
"description": "dummy_action",
|
||||
@ -228,33 +225,30 @@ class HookTestCase(test.TestCase):
|
||||
}
|
||||
}
|
||||
}
|
||||
self.assertRaises(jsonschema.ValidationError, hook.Hook.validate, conf)
|
||||
self.assertRaises(jsonschema.ValidationError, DummyHook.validate, conf)
|
||||
|
||||
@mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer)
|
||||
def test_result(self, mock_timer):
|
||||
task = mock.MagicMock()
|
||||
triggered_by = {"event_type": "iteration", "value": 1}
|
||||
dummy_hook = DummyHook(task, {"status": consts.HookStatus.SUCCESS},
|
||||
{"iteration": 1}, "dummy_action")
|
||||
triggered_by)
|
||||
dummy_hook.run_sync()
|
||||
dummy_hook._validate_result_schema()
|
||||
|
||||
self.assertEqual(
|
||||
{"description": "dummy_action",
|
||||
"hook": "dummy_hook",
|
||||
"started_at": fakes.FakeTimer().timestamp(),
|
||||
{"started_at": fakes.FakeTimer().timestamp(),
|
||||
"finished_at": fakes.FakeTimer().finish_timestamp(),
|
||||
"triggered_by": {"iteration": 1},
|
||||
"triggered_by": triggered_by,
|
||||
"status": consts.HookStatus.SUCCESS}, dummy_hook.result())
|
||||
|
||||
def test_result_not_started(self):
|
||||
task = mock.MagicMock()
|
||||
triggered_by = {"event_type": "iteration", "value": 1}
|
||||
dummy_hook = DummyHook(task, {"status": consts.HookStatus.SUCCESS},
|
||||
{"iteration": 1}, "dummy_action")
|
||||
triggered_by)
|
||||
|
||||
self.assertEqual(
|
||||
{"description": "dummy_action",
|
||||
"hook": "dummy_hook",
|
||||
"started_at": 0.0,
|
||||
{"started_at": 0.0,
|
||||
"finished_at": 0.0,
|
||||
"triggered_by": {"iteration": 1},
|
||||
"status": consts.HookStatus.UNKNOWN}, dummy_hook.result())
|
||||
"triggered_by": triggered_by,
|
||||
"status": consts.HookStatus.SUCCESS}, dummy_hook.result())
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
import ddt
|
||||
import jsonschema
|
||||
import mock
|
||||
|
||||
from rally.task import trigger
|
||||
from tests.unit import test
|
||||
@ -24,38 +25,59 @@ from tests.unit import test
|
||||
|
||||
@trigger.configure(name="dummy_trigger")
|
||||
class DummyTrigger(trigger.Trigger):
|
||||
CONFIG_SCHEMA = {"type": "integer"}
|
||||
CONFIG_SCHEMA = {"type": "array",
|
||||
"minItems": 1,
|
||||
"uniqueItems": True,
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
}}
|
||||
|
||||
def get_configured_event_type(self):
|
||||
def get_listening_event(self):
|
||||
return "dummy"
|
||||
|
||||
def is_runnable(self, value):
|
||||
return value == self.config
|
||||
def on_event(self, event_type, value=None):
|
||||
if value not in self.config:
|
||||
return
|
||||
super(DummyTrigger, self).on_event(event_type, value)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TriggerTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TriggerTestCase, self).setUp()
|
||||
self.trigger = DummyTrigger(10)
|
||||
|
||||
@ddt.data(({"name": "dummy_trigger", "args": 5}, True),
|
||||
({"name": "dummy_trigger", "args": "str"}, False))
|
||||
@ddt.data(({"name": "dummy_trigger", "args": [5]}, True),
|
||||
({"name": "dummy_trigger", "args": ["str"]}, False))
|
||||
@ddt.unpack
|
||||
def test_validate(self, config, valid):
|
||||
if valid:
|
||||
trigger.Trigger.validate(config)
|
||||
DummyTrigger.validate(config)
|
||||
else:
|
||||
self.assertRaises(jsonschema.ValidationError,
|
||||
trigger.Trigger.validate, config)
|
||||
DummyTrigger.validate, config)
|
||||
|
||||
def test_get_configured_event_type(self):
|
||||
event_type = self.trigger.get_configured_event_type()
|
||||
self.assertEqual("dummy", event_type)
|
||||
def test_on_event_and_get_results(self):
|
||||
# get_results requires launched hooks, so if we want to test it, we
|
||||
# need to duplicate all calls on_event. It is redundant, so let's merge
|
||||
# test_on_event and test_get_results in one test.
|
||||
right_values = [5, 7, 12, 13]
|
||||
|
||||
@ddt.data((10, True), (1, False))
|
||||
@ddt.unpack
|
||||
def test_is_runnable(self, value, expected_result):
|
||||
result = self.trigger.is_runnable(value)
|
||||
self.assertIs(result, expected_result)
|
||||
cfg = {"trigger": {"args": right_values}}
|
||||
task = mock.MagicMock()
|
||||
hook_cls = mock.MagicMock(__name__="fake")
|
||||
dummy_trigger = DummyTrigger(cfg, task, hook_cls)
|
||||
for i in range(0, 20):
|
||||
dummy_trigger.on_event("fake", i)
|
||||
|
||||
self.assertEqual(
|
||||
[mock.call(task, {}, {"event_type": "fake", "value": i})
|
||||
for i in right_values],
|
||||
hook_cls.call_args_list)
|
||||
self.assertEqual(len(right_values),
|
||||
hook_cls.return_value.run_async.call_count)
|
||||
hook_status = hook_cls.return_value.result.return_value["status"]
|
||||
self.assertEqual(
|
||||
{"config": cfg,
|
||||
"results": [hook_cls.return_value.result.return_value] *
|
||||
len(right_values),
|
||||
"summary": {hook_status: len(right_values)}},
|
||||
dummy_trigger.get_results())
|
||||
|
Loading…
x
Reference in New Issue
Block a user