blob: c83d130b73d038fb841a3986ffa534bd6817d63b [file] [log] [blame]
#!/usr/bin/python
# -*- coding: utf-8 -*-
# ===--- test_utils.py ---------------------------------------------------===//
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
#
# ===---------------------------------------------------------------------===//
"""
Homegrown unit testing helpers.
The issue is that the unittest.mock was added in Python 3.3 and we need to run
on Python 2.7.
`Stub` and `Mock` are very rudimentary and support only the limited subset of
common unit testing patterns that is used in this project.
"""
import logging
import sys
from StringIO import StringIO
from contextlib import contextmanager
@contextmanager
def captured_output():
"""Capture stdout and stderr and return their output as string buffers."""
new_out, new_err = StringIO(), StringIO()
old_out, old_err = sys.stdout, sys.stderr
try:
sys.stdout, sys.stderr = new_out, new_err
yield sys.stdout, sys.stderr
finally:
sys.stdout, sys.stderr = old_out, old_err
class Stub:
"""Object literal stub for value objects."""
def __init__(self, **attributes):
"""All named parameters will create properties on this object."""
self.__dict__.update(attributes)
class Mock(object):
"""Minimal infrastructure for manually mocking calls to single method.
Classes inheriting from Mock are expected to create their own mock of the
tested method with appropriate signature, which appends the call arguments
as tuple to the `calls` list and returns the canned response retrieved from
the `respond` dictionary.
"""
def __init__(self, responses=None):
"""Optionaly initialized with a list of expected calls. See expect."""
self.calls = []
self.expected = []
self.respond = dict()
responses = responses or []
for call_args, response in responses:
self.expect(call_args, response)
def expect(self, call_args, response):
"""Expect invocation of tested method with given arguments.
Stores the canned reponse in the `respond` dictionary.
"""
call_args = tuple(call_args)
self.expected.append(call_args)
self.respond[call_args] = response
def assert_called_with(self, expected_args):
"""Verify that the tested method was called with provided arguments."""
expected_args = tuple(expected_args)
assert expected_args in self.calls, (
'Expected: {0} in Called: {1}'.format(expected_args, self.calls))
def assert_called_all_expected(self):
"""Verify that all expeced invocations of tested method were called."""
assert self.calls == self.expected, (
'\nExpected: {0}, \n Called: {1}'.format(
self.expected, self.calls))
class MockLoggingHandler(logging.Handler):
"""Mock logging handler to check for expected logs."""
def __init__(self, *args, **kwargs):
"""Prepare the logger for recording messages for each log level."""
self.reset()
super(MockLoggingHandler, self).__init__(*args, **kwargs)
def emit(self, record):
"""Store the message in list for the given log level."""
self.messages[record.levelname.lower()].append(record.getMessage())
def reset(self):
"""Clear all log messages."""
self.messages = {
'debug': [], 'info': [], 'warning': [], 'error': [], 'critical': []
}