• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 Google Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import re
16import unittest
17
18from mobly import signals
19
20# Have an instance of unittest.TestCase so we could reuse some logic
21# from python's own unittest.
22_pyunit_proxy = unittest.TestCase()
23_pyunit_proxy.maxDiff = None
24
25
26def _call_unittest_assertion(assertion_method,
27                             *args,
28                             msg=None,
29                             extras=None,
30                             **kwargs):
31  """Wrapper for converting a unittest assertion into a Mobly one.
32
33  Args:
34    assertion_method: unittest.TestCase assertion method to call.
35    *args: Positional arguments for the assertion call.
36    msg: A string that adds additional info about the failure.
37    extras: An optional field for extra information to be included in
38      test result.
39    **kwargs: Keyword arguments for the assertion call.
40  """
41  my_msg = None
42  try:
43    assertion_method(*args, **kwargs)
44  except AssertionError as e:
45    my_msg = str(e)
46    if msg:
47      my_msg = f'{my_msg} {msg}'
48
49  # This raise statement is outside of the above except statement to
50  # prevent Python3's exception message from having two tracebacks.
51  if my_msg is not None:
52    raise signals.TestFailure(my_msg, extras=extras)
53
54
55def assert_equal(first, second, msg=None, extras=None):
56  """Asserts the equality of objects, otherwise fail the test.
57
58  Error message is "first != second" by default. Additional explanation can
59  be supplied in the message.
60
61  Args:
62    first: The first object to compare.
63    second: The second object to compare.
64    msg: A string that adds additional info about the failure.
65    extras: An optional field for extra information to be included in
66      test result.
67  """
68  _call_unittest_assertion(_pyunit_proxy.assertEqual,
69                           first,
70                           second,
71                           msg=msg,
72                           extras=extras)
73
74
75def assert_not_equal(first, second, msg=None, extras=None):
76  """Asserts that first is not equal (!=) to second."""
77  _call_unittest_assertion(_pyunit_proxy.assertNotEqual,
78                           first,
79                           second,
80                           msg=msg,
81                           extras=extras)
82
83
84def assert_almost_equal(first,
85                        second,
86                        places=None,
87                        msg=None,
88                        delta=None,
89                        extras=None):
90  """Asserts that first is almost equal to second.
91
92  Fails if the two objects are unequal as determined by their difference
93  rounded to the given number of decimal places (default 7) and
94  comparing to zero, or by comparing that the difference between the two
95  objects is more than the given delta.
96  If the two objects compare equal then they automatically compare
97  almost equal.
98
99  Args:
100    first: The first value to compare.
101    second: The second value to compare.
102    places: How many decimal places to take into account for comparison.
103      Note that decimal places (from zero) are usually not the same
104      as significant digits (measured from the most significant digit).
105    msg: A string that adds additional info about the failure.
106    delta: Delta to use for comparison instead of decimal places.
107    extras: An optional field for extra information to be included in
108      test result.
109  """
110  _call_unittest_assertion(_pyunit_proxy.assertAlmostEqual,
111                           first,
112                           second,
113                           places=places,
114                           msg=msg,
115                           delta=delta,
116                           extras=extras)
117
118
119def assert_not_almost_equal(first,
120                            second,
121                            places=None,
122                            msg=None,
123                            delta=None,
124                            extras=None):
125  """Asserts that first is not almost equal to second.
126
127  Args:
128    first: The first value to compare.
129    second: The second value to compare.
130    places: How many decimal places to take into account for comparison.
131      Note that decimal places (from zero) are usually not the same
132      as significant digits (measured from the most significant digit).
133    msg: A string that adds additional info about the failure.
134    delta: Delta to use for comparison instead of decimal places.
135    extras: An optional field for extra information to be included in
136      test result.
137  """
138  _call_unittest_assertion(_pyunit_proxy.assertNotAlmostEqual,
139                           first,
140                           second,
141                           places=places,
142                           msg=msg,
143                           delta=delta,
144                           extras=extras)
145
146
147def assert_in(member, container, msg=None, extras=None):
148  """Asserts that member is in container."""
149  _call_unittest_assertion(_pyunit_proxy.assertIn,
150                           member,
151                           container,
152                           msg=msg,
153                           extras=extras)
154
155
156def assert_not_in(member, container, msg=None, extras=None):
157  """Asserts that member is not in container."""
158  _call_unittest_assertion(_pyunit_proxy.assertNotIn,
159                           member,
160                           container,
161                           msg=msg,
162                           extras=extras)
163
164
165def assert_is(expr1, expr2, msg=None, extras=None):
166  """Asserts that expr1 is expr2."""
167  _call_unittest_assertion(_pyunit_proxy.assertIs,
168                           expr1,
169                           expr2,
170                           msg=msg,
171                           extras=extras)
172
173
174def assert_is_not(expr1, expr2, msg=None, extras=None):
175  """Asserts that expr1 is not expr2."""
176  _call_unittest_assertion(_pyunit_proxy.assertIsNot,
177                           expr1,
178                           expr2,
179                           msg=msg,
180                           extras=extras)
181
182
183def assert_count_equal(first, second, msg=None, extras=None):
184  """Asserts that two iterables have the same element count.
185
186  Element order does not matter.
187  Similar to assert_equal(Counter(list(first)), Counter(list(second))).
188
189  Args:
190    first: The first iterable to compare.
191    second: The second iterable to compare.
192    msg: A string that adds additional info about the failure.
193    extras: An optional field for extra information to be included in
194      test result.
195
196  Example:
197    assert_count_equal([0, 1, 1], [1, 0, 1]) passes the assertion.
198    assert_count_equal([0, 0, 1], [0, 1]) raises an assertion error.
199  """
200  _call_unittest_assertion(_pyunit_proxy.assertCountEqual,
201                           first,
202                           second,
203                           msg=msg,
204                           extras=extras)
205
206
207def assert_less(a, b, msg=None, extras=None):
208  """Asserts that a < b."""
209  _call_unittest_assertion(_pyunit_proxy.assertLess,
210                           a,
211                           b,
212                           msg=msg,
213                           extras=extras)
214
215
216def assert_less_equal(a, b, msg=None, extras=None):
217  """Asserts that a <= b."""
218  _call_unittest_assertion(_pyunit_proxy.assertLessEqual,
219                           a,
220                           b,
221                           msg=msg,
222                           extras=extras)
223
224
225def assert_greater(a, b, msg=None, extras=None):
226  """Asserts that a > b."""
227  _call_unittest_assertion(_pyunit_proxy.assertGreater,
228                           a,
229                           b,
230                           msg=msg,
231                           extras=extras)
232
233
234def assert_greater_equal(a, b, msg=None, extras=None):
235  """Asserts that a >= b."""
236  _call_unittest_assertion(_pyunit_proxy.assertGreaterEqual,
237                           a,
238                           b,
239                           msg=msg,
240                           extras=extras)
241
242
243def assert_is_none(obj, msg=None, extras=None):
244  """Asserts that obj is None."""
245  _call_unittest_assertion(_pyunit_proxy.assertIsNone,
246                           obj,
247                           msg=msg,
248                           extras=extras)
249
250
251def assert_is_not_none(obj, msg=None, extras=None):
252  """Asserts that obj is not None."""
253  _call_unittest_assertion(_pyunit_proxy.assertIsNotNone,
254                           obj,
255                           msg=msg,
256                           extras=extras)
257
258
259def assert_is_instance(obj, cls, msg=None, extras=None):
260  """Asserts that obj is an instance of cls."""
261  _call_unittest_assertion(_pyunit_proxy.assertIsInstance,
262                           obj,
263                           cls,
264                           msg=msg,
265                           extras=extras)
266
267
268def assert_not_is_instance(obj, cls, msg=None, extras=None):
269  """Asserts that obj is not an instance of cls."""
270  _call_unittest_assertion(_pyunit_proxy.assertNotIsInstance,
271                           obj,
272                           cls,
273                           msg=msg,
274                           extras=extras)
275
276
277def assert_regex(text, expected_regex, msg=None, extras=None):
278  """Fails the test unless the text matches the regular expression."""
279  _call_unittest_assertion(_pyunit_proxy.assertRegex,
280                           text,
281                           expected_regex,
282                           msg=msg,
283                           extras=extras)
284
285
286def assert_not_regex(text, unexpected_regex, msg=None, extras=None):
287  """Fails the test if the text matches the regular expression."""
288  _call_unittest_assertion(_pyunit_proxy.assertNotRegex,
289                           text,
290                           unexpected_regex,
291                           msg=msg,
292                           extras=extras)
293
294
295def assert_raises(expected_exception, extras=None, *args, **kwargs):
296  """Assert that an exception is raised when a function is called.
297
298  If no exception is raised, test fail. If an exception is raised but not
299  of the expected type, the exception is let through.
300
301  This should only be used as a context manager:
302    with assert_raises(Exception):
303      func()
304
305  Args:
306    expected_exception: An exception class that is expected to be
307      raised.
308    extras: An optional field for extra information to be included in
309      test result.
310  """
311  context = _AssertRaisesContext(expected_exception, extras=extras)
312  return context
313
314
315def assert_raises_regex(expected_exception,
316                        expected_regex,
317                        extras=None,
318                        *args,
319                        **kwargs):
320  """Assert that an exception is raised when a function is called.
321
322  If no exception is raised, test fail. If an exception is raised but not
323  of the expected type, the exception is let through. If an exception of the
324  expected type is raised but the error message does not match the
325  expected_regex, test fail.
326
327  This should only be used as a context manager:
328    with assert_raises(Exception):
329      func()
330
331  Args:
332    expected_exception: An exception class that is expected to be
333      raised.
334    extras: An optional field for extra information to be included in
335      test result.
336  """
337  context = _AssertRaisesContext(expected_exception,
338                                 expected_regex,
339                                 extras=extras)
340  return context
341
342
343def assert_true(expr, msg, extras=None):
344  """Assert an expression evaluates to true, otherwise fail the test.
345
346  Args:
347    expr: The expression that is evaluated.
348    msg: A string explaining the details in case of failure.
349    extras: An optional field for extra information to be included in
350      test result.
351  """
352  if not expr:
353    fail(msg, extras)
354
355
356def assert_false(expr, msg, extras=None):
357  """Assert an expression evaluates to false, otherwise fail the test.
358
359  Args:
360    expr: The expression that is evaluated.
361    msg: A string explaining the details in case of failure.
362    extras: An optional field for extra information to be included in
363      test result.
364  """
365  if expr:
366    fail(msg, extras)
367
368
369def skip(reason, extras=None):
370  """Skip a test.
371
372  Args:
373    reason: The reason this test is skipped.
374    extras: An optional field for extra information to be included in
375      test result.
376
377  Raises:
378    signals.TestSkip: Mark a test as skipped.
379  """
380  raise signals.TestSkip(reason, extras)
381
382
383def skip_if(expr, reason, extras=None):
384  """Skip a test if expression evaluates to True.
385
386  Args:
387    expr: The expression that is evaluated.
388    reason: The reason this test is skipped.
389    extras: An optional field for extra information to be included in
390      test result.
391  """
392  if expr:
393    skip(reason, extras)
394
395
396def abort_class(reason, extras=None):
397  """Abort all subsequent tests within the same test class in one iteration.
398
399  If one test class is requested multiple times in a test run, this can
400  only abort one of the requested executions, NOT all.
401
402  Args:
403    reason: The reason to abort.
404    extras: An optional field for extra information to be included in
405      test result.
406
407  Raises:
408    signals.TestAbortClass: Abort all subsequent tests in a test class.
409  """
410  raise signals.TestAbortClass(reason, extras)
411
412
413def abort_class_if(expr, reason, extras=None):
414  """Abort all subsequent tests within the same test class in one iteration,
415  if expression evaluates to True.
416
417  If one test class is requested multiple times in a test run, this can
418  only abort one of the requested executions, NOT all.
419
420  Args:
421    expr: The expression that is evaluated.
422    reason: The reason to abort.
423    extras: An optional field for extra information to be included in
424      test result.
425
426  Raises:
427    signals.TestAbortClass: Abort all subsequent tests in a test class.
428  """
429  if expr:
430    abort_class(reason, extras)
431
432
433def abort_all(reason, extras=None):
434  """Abort all subsequent tests, including the ones not in this test class or
435  iteration.
436
437  Args:
438    reason: The reason to abort.
439    extras: An optional field for extra information to be included in
440      test result.
441
442  Raises:
443    signals.TestAbortAll: Abort all subsequent tests.
444  """
445  raise signals.TestAbortAll(reason, extras)
446
447
448def abort_all_if(expr, reason, extras=None):
449  """Abort all subsequent tests, if the expression evaluates to True.
450
451  Args:
452    expr: The expression that is evaluated.
453    reason: The reason to abort.
454    extras: An optional field for extra information to be included in
455      test result.
456
457  Raises:
458    signals.TestAbortAll: Abort all subsequent tests.
459  """
460  if expr:
461    abort_all(reason, extras)
462
463
464def fail(msg, extras=None):
465  """Explicitly fail a test.
466
467  Args:
468    msg: A string explaining the details of the failure.
469    extras: An optional field for extra information to be included in
470      test result.
471
472  Raises:
473    signals.TestFailure: Mark a test as failed.
474  """
475  raise signals.TestFailure(msg, extras)
476
477
478def explicit_pass(msg, extras=None):
479  """Explicitly pass a test.
480
481  This will pass the test explicitly regardless of any other error happened
482  in the test body. E.g. even if errors have been recorded with `expects`,
483  the test will still be marked pass if this is called.
484
485  A test without uncaught exception will pass implicitly so this should be
486  used scarcely.
487
488  Args:
489    msg: A string explaining the details of the passed test.
490    extras: An optional field for extra information to be included in
491      test result.
492
493  Raises:
494    signals.TestPass: Mark a test as passed.
495  """
496  raise signals.TestPass(msg, extras)
497
498
499class _AssertRaisesContext:
500  """A context manager used to implement TestCase.assertRaises* methods."""
501
502  def __init__(self, expected, expected_regexp=None, extras=None):
503    self.expected = expected
504    self.failureException = signals.TestFailure
505    self.expected_regexp = expected_regexp
506    self.extras = extras
507
508  def __enter__(self):
509    return self
510
511  def __exit__(self, exc_type, exc_value, tb):
512    if exc_type is None:
513      try:
514        exc_name = self.expected.__name__
515      except AttributeError:
516        exc_name = str(self.expected)
517      raise signals.TestFailure('%s not raised' % exc_name, extras=self.extras)
518    if not issubclass(exc_type, self.expected):
519      # let unexpected exceptions pass through
520      return False
521    self.exception = exc_value  # store for later retrieval
522    if self.expected_regexp is None:
523      return True
524
525    expected_regexp = self.expected_regexp
526    if isinstance(expected_regexp, str):
527      expected_regexp = re.compile(expected_regexp)
528    if not expected_regexp.search(str(exc_value)):
529      raise signals.TestFailure('"%s" does not match "%s"' %
530                                (expected_regexp.pattern, str(exc_value)),
531                                extras=self.extras)
532    return True
533