tester.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 import rosunit
3 import unittest
4 import re
5 
6 from flexbe_core.core.user_data import UserData
7 
8 from .logger import Logger
9 from .test_interface import TestInterface
10 from .test_context import TestContext, LaunchContext
11 from .data_provider import DataProvider
12 
13 
14 class Tester(object):
15 
16  def __init__(self):
17  self._tests = dict()
18 
19  def run_test(self, name, config):
20  try:
21  self._verify_config(config)
22  except Exception as e:
23  Logger.print_title(name, 'Invalid', None)
24  Logger.print_error('invalid test specification!\n\t%s' % str(e))
25  Logger.print_result(name, False)
26  self._tests['test_%s_pass' % name] = self._test_config_invalid(str(e))
27  return 0
28 
29  # allow to specify behavior name instead of generated module and class
30  if 'name' in config:
31  config['path'] += '.%s_sm' % re.sub(r'[^\w]', '_', config['name'].lower())
32  config['class'] = '%sSM' % re.sub(r'[^\w]', '', config['name'])
33 
34  import_only = config.get('import_only', False)
35  Logger.print_title(name, config['class'], config['outcome'] if not import_only else None)
36 
37  # import test subject
38  try:
39  test_interface = TestInterface(config['path'], config['class'])
40  except Exception as e:
41  Logger.print_failure('unable to import state %s (%s):\n\t%s' %
42  (config['class'], config['path'], str(e)))
43  self._tests['test_%s_pass' % name] = self._test_pass(False)
44  return 0
45 
46  if not import_only:
47  # prepare test context
48  context = None
49  if 'launch' in config:
50  context = LaunchContext(config['launch'], config.get('wait_cond', 'True'))
51  else:
52  context = TestContext()
53 
54  # load data source
55  try:
56  data = DataProvider(bagfile=config.get('data', None))
57  except Exception as e:
58  Logger.print_failure('unable to load data source %s:\n\t%s' %
59  (config['data'], str(e)))
60  self._tests['test_%s_pass' % name] = self._test_pass(False)
61  return 0
62 
63  # run test context
64  with context:
65  if not context.verify():
66  Logger.print_error('failed to initialize test context:\n\t%s' % config['launch'])
67  self._tests['test_%s_pass' % name] = self._test_pass(False)
68  return 0
69 
70  # instantiate test subject
71  params = {key: data.parse(value) for key, value in list(config.get('params', dict()).items())}
72  try:
73  test_interface.instantiate(params)
74  except Exception as e:
75  Logger.print_failure('unable to instantiate %s (%s) with params:\n\t%s\n\t%s' %
76  (config['class'], config['path'], str(params), str(e)))
77  self._tests['test_%s_pass' % name] = self._test_pass(False)
78  return 0
79 
80  # prepare user data
81  userdata = UserData()
82  for input_key, input_value in list(config.get('input', dict()).items()):
83  userdata[input_key] = data.parse(input_value)
84  expected = {key: data.parse(value) for key, value in config.get('output', dict()).items()}
85 
86  # run test subject
87  try:
88  outcome = test_interface.execute(userdata, spin_cb=context.spin_once)
89  except Exception as e:
90  Logger.print_failure('failed to execute %s (%s)\n\t%s' %
91  (config['class'], config['path'], str(e)))
92  self._tests['test_%s_pass' % name] = self._test_pass(False)
93  return 0
94 
95  if config.get('require_launch_success', False):
96  context.wait_for_finishing()
97 
98  # evaluate outcome
99  self._tests['test_%s_outcome' % name] = self._test_outcome(outcome, config['outcome'])
100  outcome_ok = outcome == config['outcome']
101  if outcome_ok:
102  Logger.print_positive('correctly returned outcome %s' % outcome)
103  else:
104  Logger.print_negative('wrong outcome: %s' % outcome)
105 
106  # evaluate output
107  output_ok = True
108  for expected_key, expected_value in list(expected.items()):
109  if expected_key in userdata:
110  equals = userdata[expected_key] == expected_value
111  self._tests['test_%s_output_%s' % (name, expected_key)] = \
112  self._test_output(userdata[expected_key], expected_value)
113  if not equals:
114  Logger.print_negative('wrong result for %s: %s != %s' %
115  (expected_key, userdata[expected_key], expected_value))
116  output_ok = False
117  else:
118  Logger.print_negative('no result for %s' % expected_key)
119  output_ok = False
120 
121  if not context.success and config.get('require_launch_success', False):
122  Logger.print_negative('Launch file did not exit cleanly')
123  output_ok = False
124 
125  if len(expected) > 0 and output_ok:
126  Logger.print_positive('all result outputs match expected')
127 
128  # report result
129  success = import_only or outcome_ok and output_ok
130  Logger.print_result(name, success)
131  self._tests['test_%s_pass' % name] = self._test_pass(success)
132  return 1 if success else 0
133 
134  def _verify_config(self, config):
135  if not isinstance(config, dict):
136  raise AssertionError('config needs to be a dictionary but is:\n\t%s' % str(config))
137  assert 'path' in config
138  assert 'class' in config or 'name' in config
139  assert 'outcome' in config or config.get('import_only', False)
140 
141  # ROSUNIT interface
142 
143  def perform_rostest(self, test_pkg):
144  TestCase = type(test_pkg + '_test_class', (unittest.TestCase,), self._tests)
145  rosunit.unitrun(test_pkg, test_pkg + '_flexbe_tests', TestCase)
146 
147  def _test_output(self, value, expected):
148  def _test_call(test_self):
149  test_self.assertEqual(value, expected, "Output value %s does not match expected %s" % (value, expected))
150  return _test_call
151 
152  def _test_outcome(self, outcome, expected):
153  def _test_call(test_self):
154  test_self.assertEqual(outcome, expected, "Outcome %s does not match expected %s" % (outcome, expected))
155  return _test_call
156 
157  def _test_pass(self, passed):
158  def _test_call(test_self):
159  test_self.assertTrue(passed, "Did not pass configured tests.")
160  return _test_call
161 
162  def _test_config_invalid(self, config):
163  def _test_call(test_self):
164  test_self.fail("Test config is invalid: %s" % config)
165  return _test_call
def _test_outcome(self, outcome, expected)
Definition: tester.py:152
def _test_pass(self, passed)
Definition: tester.py:157
def _test_config_invalid(self, config)
Definition: tester.py:162
def run_test(self, name, config)
Definition: tester.py:19
def _test_output(self, value, expected)
Definition: tester.py:147
def perform_rostest(self, test_pkg)
Definition: tester.py:143
def _verify_config(self, config)
Definition: tester.py:134


flexbe_testing
Author(s): Philipp Schillinger
autogenerated on Sun Dec 13 2020 04:01:44