Пример #1
0
 def setUp(self):
     self.tape_cassette = InMemoryTapeCassette()
     self.tape_recorder = TapeRecorder(self.tape_cassette)
     self.tape_recorder.enable_recording()
Пример #2
0
class TestTapeRecorder(unittest.TestCase):

    def setUp(self):
        self.tape_cassette = InMemoryTapeCassette()
        self.tape_recorder = TapeRecorder(self.tape_cassette)
        self.tape_recorder.enable_recording()

    def test_record_and_playback_basic_operation_no_parameters(self):
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                return 5

        instance = Operation()
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def _assert_playback_vs_recording(self, playback_result, result):
        """
        :param playback_result: Playback result
        :type playback_result: playback.tape_recorder.Playback
        :param result: Operation result
        :type result: Any
        """
        if six.PY3:
            self.assertCountEqual(playback_result.recorded_outputs, playback_result.playback_outputs)
        else:
            self.assertItemsEqual(playback_result.recorded_outputs, playback_result.playback_outputs)

        operation_output = next(po for po in playback_result.playback_outputs
                                if TapeRecorder.OPERATION_OUTPUT_ALIAS in po.key)
        self.assertEqual(result, operation_output.value['args'][0])
        self.assertGreater(playback_result.playback_duration, 0)
        self.assertGreater(playback_result.recorded_duration, 0)

    def test_record_and_playback_basic_operation_data_interception_no_arguments(self):
        class Operation(object):

            def __init__(self, seed=0):
                self.seed = seed

            @self.tape_recorder.operation()
            def execute(self):
                return self.get_value()

            @self.tape_recorder.intercept_input('input')
            def get_value(self):
                return self.seed

        instance = Operation(5)
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_record_and_playback_basic_operation_data_interception_property(self):
        class Operation(object):

            def __init__(self, seed=0):
                self.seed = seed

            @self.tape_recorder.operation()
            def execute(self):
                return self.get_value

            @self.tape_recorder.intercept_input('input')
            @property
            def get_value(self):
                return self.seed

        instance = Operation(5)
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_record_and_playback_basic_operation_data_interception_with_arguments(self):
        class Operation(object):

            def __init__(self, seed=0):
                self.seed = seed

            @self.tape_recorder.operation()
            def execute(self):
                val1 = self.get_value(2, b=3)
                val2 = self.get_value(4, b=6)
                return val1 + val2

            @self.tape_recorder.intercept_input('input')
            def get_value(self, a, b=2):
                return (a + b) * self.seed

        instance = Operation(seed=1)
        result = instance.execute()
        self.assertEqual(15, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_record_and_playback_basic_operation_static_data_interception_with_arguments(self):
        class Operation(object):

            seed = 0

            @self.tape_recorder.operation()
            def execute(self):
                val1 = self.get_value(2, b=3)
                val2 = self.get_value(4, b=6)
                return val1 + val2

            @staticmethod
            @self.tape_recorder.static_intercept_input('input')
            def get_value(a, b=2):
                return (a + b) * Operation.seed

        instance = Operation()
        Operation.seed = 1
        result = instance.execute()
        self.assertEqual(15, result)

        Operation.seed = 0
        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_record_and_playback_basic_operation_data_interception_with_arguments_and_metadata(self):
        def operation_metadata_extractor(obj):
            return {'type': type(obj)}

        class Operation(object):

            def __init__(self, seed=0):
                self.seed = seed

            @self.tape_recorder.operation(metadata_extractor=operation_metadata_extractor)
            def execute(self):
                val1 = self.get_value(2, b=3)
                val2 = self.get_value(4, b=6)
                return val1 + val2

            @self.tape_recorder.intercept_input('input')
            def get_value(self, a, b=2):
                return (a + b) * self.seed

        instance = Operation(seed=1)
        result = instance.execute()
        self.assertEqual(15, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        recording = self.tape_cassette.get_recording(recording_id)
        # pickle encode decode doesn't reconstruct actuall class if this is an inner class
        operation_decoded = decode(encode(Operation, unpicklable=False))
        self.assertDictContainsSubset({'type': operation_decoded, TapeRecorder.OPERATION_CLASS: operation_decoded},
                                      recording.get_metadata())
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_record_and_playback_basic_operation_no_parameters_with_output(self):
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                x = 0
                x += self.output(4, arg='a')
                x += self.output(3, arg='b')
                return x

            @self.tape_recorder.intercept_output('output_function')
            def output(self, value, arg=None):
                return value

        instance = Operation()
        result = instance.execute()
        self.assertEqual(7, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())

        self._assert_playback_vs_recording(playback_result, result)

        self.assertEqual({'args': [4], 'kwargs': {'arg': 'a'}}, playback_result.playback_outputs[0].value)
        self.assertEqual({'args': [3], 'kwargs': {'arg': 'b'}}, playback_result.playback_outputs[1].value)
        self.assertNotEqual(playback_result.playback_outputs[0].key, playback_result.playback_outputs[1].key)
        self.assertIn('output_function', playback_result.playback_outputs[0].key)
        self.assertIn('output_function', playback_result.playback_outputs[1].key)

    def test_record_and_playback_basic_operation_no_parameters_with_static_output(self):
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                x = 0
                x += self.output(4, arg='a')
                x += self.output(3, arg='b')
                return x

            @staticmethod
            @self.tape_recorder.static_intercept_output('output_function')
            def output(value, arg=None):
                return value

        instance = Operation()
        result = instance.execute()
        self.assertEqual(7, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())

        self._assert_playback_vs_recording(playback_result, result)

        self.assertEqual({'args': [4], 'kwargs': {'arg': 'a'}}, playback_result.playback_outputs[0].value)
        self.assertEqual({'args': [3], 'kwargs': {'arg': 'b'}}, playback_result.playback_outputs[1].value)
        self.assertNotEqual(playback_result.playback_outputs[0].key, playback_result.playback_outputs[1].key)
        self.assertIn('output_function', playback_result.playback_outputs[0].key)
        self.assertIn('output_function', playback_result.playback_outputs[1].key)

    def test_record_and_playback_basic_operation_similar_duration(self):
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                sleep(0.5)
                return 5

        instance = Operation()
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self.assertLessEqual(abs(playback_result.recorded_duration - playback_result.playback_duration), 0.1)

    def test_record_and_playback_basic_operation_class_extraction_in_metadata(self):
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                return 5

            @classmethod
            @self.tape_recorder.class_operation()
            def class_execute(cls):
                return 5

        instance = Operation()
        self.assertEqual(5, instance.execute())

        recording_id = self.tape_cassette.get_last_recording_id()
        recording = self.tape_cassette.get_recording(recording_id)
        # pickle encode decode doesn't reconstruct actuall class if this is an inner class
        operation_decoded = decode(encode(Operation, unpicklable=False))
        self.assertDictContainsSubset({TapeRecorder.OPERATION_CLASS: operation_decoded}, recording.get_metadata())

        self.assertEqual(5, instance.class_execute())

        recording_id = self.tape_cassette.get_last_recording_id()
        recording = self.tape_cassette.get_recording(recording_id)
        # Inner classes are not unpicklable back to class upon decode
        self.assertDictContainsSubset({TapeRecorder.OPERATION_CLASS: operation_decoded}, recording.get_metadata())

    def test_drop_recording_direct_api_call(self):
        local_tape_recorder = self.tape_recorder

        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                local_tape_recorder.discard_recording()
                return 5

        instance = Operation()
        with patch.object(InMemoryTapeCassette, '_save_recording', wraps=self.tape_cassette._save_recording) \
                as intercepted_save, \
                patch.object(InMemoryTapeCassette, 'abort_recording', wraps=self.tape_cassette.abort_recording) \
                as intercepted_abort:
            result = instance.execute()
            intercepted_save.assert_not_called()
            intercepted_abort.assert_called()

        self.assertEqual(5, result)

    def test_skip_recording_decorator(self):

        @self.tape_recorder.recording_params(skipped=True)
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                return 5

        instance = Operation()
        with patch.object(InMemoryTapeCassette, 'create_new_recording', wraps=self.tape_cassette.create_new_recording) \
                as intercepted:
            result = instance.execute()
            intercepted.assert_not_called()

        self.assertEqual(5, result)

    def test_input_interception_key_failure(self):

        class UnencodeableObject(object):
            def __getstate__(self):
                raise Exception()

        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                param = UnencodeableObject()
                value = self.input(param)
                return value

            @self.tape_recorder.intercept_input('input')
            def input(self, param):
                return 5

        instance = Operation()
        with patch.object(InMemoryTapeCassette, '_save_recording', wraps=self.tape_cassette._save_recording) \
                as intercepted_save, \
                patch.object(InMemoryTapeCassette, 'abort_recording', wraps=self.tape_cassette.abort_recording) \
                as intercepted_abort:
            result = instance.execute()
            intercepted_save.assert_not_called()
            intercepted_abort.assert_called()

        self.assertEqual(5, result)

    def test_input_interception_key_failure_during_playback(self):

        class UnencodeableObject(object):
            def __getstate__(self):
                raise Exception()

        class Operation(object):

            def __init__(self, raise_on_get=False):
                self.raise_on_get = raise_on_get

            @self.tape_recorder.operation()
            def execute(self):
                param = UnencodeableObject() if self.raise_on_get else object()
                value = self.input(param)
                return value

            @self.tape_recorder.intercept_input('input')
            def input(self, param):
                return 5

        instance = Operation()
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        with self.assertRaises(InputInterceptionKeyCreationError):
            self.tape_recorder.play(recording_id, playback_function=lambda recording: Operation(True).execute())

    def test_input_interception_key_missing_during_playback(self):

        class Operation(object):

            def __init__(self, input_key):
                self.input_key = input_key

            @self.tape_recorder.operation()
            def execute(self):
                value = self.input(self.input_key)
                return value

            @self.tape_recorder.intercept_input('input')
            def input(self, param):
                return 5

        instance = Operation('key1')
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        with self.assertRaises(RecordingKeyError):
            self.tape_recorder.play(recording_id, playback_function=lambda recording: Operation('key2').execute())

    def test_output_interception_key_missing_during_playback(self):

        class Operation(object):

            def __init__(self, output_method):
                self.output_method = output_method

            @self.tape_recorder.operation()
            def execute(self):
                value = getattr(self, self.output_method)()
                return value

            @self.tape_recorder.intercept_output('output_function')
            def output(self):
                return 5

            @self.tape_recorder.intercept_output('output_missing')
            def output_missing(self):
                return 5

        instance = Operation('output')
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        with self.assertRaises(RecordingKeyError):
            self.tape_recorder.play(recording_id,
                                    playback_function=lambda recording: Operation('output_missing').execute())

    def test_record_and_playback_basic_operation_metadata_extractor_raise_exception(self):
        def operation_metadata_extractor(*args, **kwargs):
            raise Exception('Meta exception')

        class Operation(object):

            @self.tape_recorder.operation(metadata_extractor=operation_metadata_extractor)
            def execute(self):
                return 5

        instance = Operation()
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_record_and_playback_basic_operation_save_recording_raise_exception(self):
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                return 5

        with patch.object(InMemoryTapeCassette, '_save_recording', side_effect=Exception()):
            instance = Operation()
            result = instance.execute()
            self.assertEqual(5, result)

    def test_record_and_playback_basic_operation_no_parameters_raise_error(self):
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                raise ValueError("Error")

        instance = Operation()
        with self.assertRaises(ValueError) as e:
            instance.execute()
        self.assertEqual("Error", str(e.exception))

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        operation_output = next(po for po in playback_result.playback_outputs
                                if TapeRecorder.OPERATION_OUTPUT_ALIAS in po.key)
        self.assertEqual(ValueError, type(operation_output.value['args'][0]))
        self.assertEqual("Error", str(operation_output.value['args'][0]))
        self.assertEqual(len(playback_result.recorded_outputs), len(playback_result.playback_outputs))
        self.assertGreater(playback_result.playback_duration, 0)
        self.assertGreater(playback_result.recorded_duration, 0)
        self.assertDictContainsSubset({TapeRecorder.EXCEPTION_IN_OPERATION: True},
                                      playback_result.original_recording.get_metadata())

    def test_record_and_playback_basic_operation_data_interception_no_arguments_raise_exception(self):
        class Operation(object):

            def __init__(self, seed=0):
                self.seed = seed

            @self.tape_recorder.operation()
            def execute(self):
                try:
                    self.get_value()
                except Exception as ex:
                    return str(ex)

            @self.tape_recorder.intercept_input('input')
            def get_value(self):
                raise Exception(self.seed)

        instance = Operation(5)
        result = instance.execute()
        self.assertEqual('5', result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_record_and_playback_basic_operation_output_interception_no_arguments_raise_exception(self):
        class Operation(object):

            def __init__(self, seed=0):
                self.seed = seed

            @self.tape_recorder.operation()
            def execute(self):
                try:
                    self.output()
                except Exception as ex:
                    return str(ex)

            @self.tape_recorder.intercept_output('output_function')
            def output(self):
                raise Exception(self.seed)

        instance = Operation(5)
        result = instance.execute()
        self.assertEqual("5", result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_sampling_rate_class_level_decorator(self):

        @self.tape_recorder.recording_params(sampling_rate=0.1)
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                return 5

        instance = Operation()
        with patch.object(InMemoryTapeCassette, 'save_recording', wraps=self.tape_cassette.create_new_recording) \
                as intercepted:
            calls = 100
            for __ in range(calls):
                result = instance.execute()
                self.assertEqual(5, result)

            call_ratio = float(intercepted.call_count)/calls
            print('Call ratio {}'.format(call_ratio))
            self.assertAlmostEqual(0.1, call_ratio, places=1)

    def test_operation_with_input_dict_as_key(self):
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                # We are shuffling here hoping to get different internal order for the dict between calls but still
                # see we are consistent on input interception is these are equivalent dicts
                items = list(range(100))
                shuffle(items)
                argument = {'key{}'.format(i): i for i in items}
                return self.get_value(argument)

            @self.tape_recorder.intercept_input('input')
            def get_value(self, json_dict):
                return json_dict['key5']

        instance = Operation()
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_record_and_playback_operation_with_alias_param_resolver(self):
        class ValueCreator(object):
            def __init__(self, name, value):
                self.name = name
                self.value = value

            @self.tape_recorder.intercept_input('input.{name}', alias_params_resolver=lambda s: {'name': s.name})
            def get_value(self):
                return self.value

        class Operation(object):

            def __init__(self, seed=0):
                self.seed = seed

            @self.tape_recorder.operation()
            def execute(self):
                # We check that we are capturing different value interceptions even though we pass same arguments to
                # the intercepted method by using a param resolver that will provide unique interception key for each
                # invocation
                return self.get_value('a', self.seed) + self.get_value('b', self.seed * 2)

            @staticmethod
            def get_value(name, value):
                return ValueCreator(name, value).get_value()

        instance = Operation(5)
        result = instance.execute()
        self.assertEqual(15, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_record_and_playback_operation_with_input_interception_data_handler(self):
        test_self = self

        class XorDataHandler(InputInterceptionDataHandler):

            def prepare_input_for_recording(self, interception_key, result, args, kwargs):
                test_self.assertIn('value_input', interception_key)
                test_self.assertEqual(5, result)
                test_self.assertEqual(('x', ), args[1:])
                test_self.assertEqual({'b': 'y'}, kwargs)
                return 5 ^ 3141

            def restore_input_from_recording(self, recorded_data, args, kwargs):
                # Applying same xor twice return original result
                return recorded_data ^ 3141

        class Operation(object):

            def __init__(self, seed=0):
                self.seed = seed

            @self.tape_recorder.operation()
            def execute(self):
                return self.get_value('x', b='y')

            @self.tape_recorder.intercept_input('value_input', data_handler=XorDataHandler())
            def get_value(self, a, b=None):
                return self.seed

        instance = Operation(5)
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        # Check the recording it self contains the modified value
        recording = self.tape_cassette.get_recording(recording_id)
        key = next(k for k in recording.get_all_keys() if 'value_input' in k)
        self.assertEqual(5 ^ 3141, recording.get_data(key)['value'])

        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_input_interception_data_handler_failure(self):

        class ErrorDataHandler(InputInterceptionDataHandler):

            def prepare_input_for_recording(self, interception_key, result, args, kwargs):
                raise Exception('error')

            def restore_input_from_recording(self, recorded_data, args, kwargs):
                pass

        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                return self.get_value() + self.get_value2()

            @self.tape_recorder.intercept_input('value_input', data_handler=ErrorDataHandler())
            def get_value(self):
                return 5

            @self.tape_recorder.intercept_input('value_input2')
            def get_value2(self):
                return 5

        instance = Operation()
        with patch.object(InMemoryTapeCassette, '_save_recording', wraps=self.tape_cassette._save_recording) \
                as intercepted:
            result = instance.execute()
            intercepted.assert_not_called()

        self.assertEqual(10, result)

    def test_record_and_playback_basic_operation_data_interception_with_all_arguments_ignored_as_key(self):
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                arg1 = random()
                arg2 = random()
                val1 = self.get_value(arg1, b=arg2)
                return val1

            @self.tape_recorder.intercept_input('input', capture_args=[])
            def get_value(self, a, b=2):
                return 5

        instance = Operation()
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_record_and_playback_basic_operation_data_interception_with_false_capture_args(self):
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                arg1 = random()
                arg2 = random()
                val1 = self.get_value(arg1, b=arg2)
                return val1

            @self.tape_recorder.intercept_input('input', capture_args=False)
            def get_value(self, a, b=2):
                return 5

        instance = Operation()
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_sampling_rate_with_enforced_recording(self):
        local_tape_recorder = self.tape_recorder
        test_self = self

        @self.tape_recorder.recording_params(sampling_rate=0.2)
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                local_tape_recorder.force_sample_recording()
                test_self.assertTrue(local_tape_recorder.is_recording_sample_forced)
                return 5

        instance = Operation()
        with patch.object(InMemoryTapeCassette, '_save_recording', wraps=self.tape_cassette._save_recording) \
                as intercepted:
            calls = 10
            for __ in range(calls):
                result = instance.execute()
                self.assertEqual(5, result)

            call_ratio = float(intercepted.call_count)/calls
            print('Call ratio {}'.format(call_ratio))
            self.assertEqual(1, call_ratio)

    def test_sampling_rate_with_ignore_enforced_recording(self):
        local_tape_recorder = self.tape_recorder
        test_self = self

        @self.tape_recorder.recording_params(sampling_rate=0.1, ignore_enforced_sampling=True)
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                local_tape_recorder.force_sample_recording()
                test_self.assertFalse(local_tape_recorder.is_recording_sample_forced)
                return 5

        instance = Operation()
        with patch.object(InMemoryTapeCassette, '_save_recording', wraps=self.tape_cassette._save_recording) \
                as intercepted_save,\
                patch.object(InMemoryTapeCassette, 'abort_recording', wraps=self.tape_cassette.abort_recording) \
                as intercepted_abort:
            calls = 100
            for __ in range(calls):
                result = instance.execute()
                self.assertEqual(5, result)

            expected_aborted = calls - intercepted_save.call_count
            call_ratio = float(intercepted_save.call_count) / calls
            print('Call ratio {}'.format(call_ratio))
            self.assertAlmostEqual(0.1, call_ratio, places=1)
            self.assertEqual(expected_aborted, intercepted_abort.call_count)

    def test_record_and_playback_basic_operation_data_interception_with_specific_arguments_ignored_as_key(self):
        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                # This is random to see that capture args is only capturing the requested arg index
                arg1 = random()
                arg2 = 'b'
                arg3 = 'c'
                # This is random to see that capture args is only capturing the requested arg kwargs
                arg4 = random()
                arg5 = 'e'
                val1 = self.get_value(arg1, arg2, c=arg3, d=arg4, e=arg5)
                return val1

            @self.tape_recorder.intercept_input('input', capture_args=[CapturedArg(2, 'b'), CapturedArg(3, 'c'),
                                                                       CapturedArg(None, 'e'), CapturedArg(None, 'f')])
            def get_value(self, a, b, c, d=None, e=None, f=None):
                return 5

        instance = Operation()
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

    def test_record_and_playback_operation_with_output_interception_data_handler(self):
        test_self = self

        class XorDataHandler(OutputInterceptionDataHandler):

            def prepare_output_for_recording(self, interception_key, args, kwargs):
                test_self.assertIn('output_function', interception_key)
                test_self.assertEqual((5, ), args)
                test_self.assertEqual({'arg': 'a'}, kwargs)
                return args[0] ^ 3141

            def restore_output_from_recording(self, recorded_data):
                # Applying same xor twice return original result
                return recorded_data ^ 3141

        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                return self.output(5, arg='a')

            @self.tape_recorder.intercept_output('output_function', data_handler=XorDataHandler())
            def output(self, value, arg=None):
                return value

        instance = Operation()
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        # Check the recording it self contains the modified value
        recording = self.tape_cassette.get_recording(recording_id)
        key = next(k for k in recording.get_all_keys() if 'output_function' in k)
        self.assertEqual(5 ^ 3141, recording.get_data(key))
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())

        self._assert_playback_vs_recording(playback_result, result)

        self.assertEqual(result, XorDataHandler().restore_output_from_recording(
            playback_result.playback_outputs[0].value))

    def test_output_interception_data_handler_failure(self):

        class ErrorDataHandler(OutputInterceptionDataHandler):

            def prepare_output_for_recording(self, interception_key, args, kwargs):
                raise Exception('error')

            def restore_output_from_recording(self, recorded_data):
                pass

        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                return self.output(5) + self.output2(5)

            @self.tape_recorder.intercept_output('output_function', data_handler=ErrorDataHandler())
            def output(self, value):
                return value

            @self.tape_recorder.intercept_output('output_function2')
            def output2(self, value):
                return value

        instance = Operation()
        with patch.object(InMemoryTapeCassette, '_save_recording', wraps=self.tape_cassette._save_recording) \
                as intercepted:
            result = instance.execute()
            intercepted.assert_not_called()

        self.assertEqual(10, result)

    def test_record_and_playback_basic_operation_data_interception_inside_interception(self):
        class Operation(object):

            def __init__(self, seed=0):
                self.seed = seed

            @self.tape_recorder.operation()
            def execute(self):
                return self.get_value()

            @self.tape_recorder.intercept_input('input')
            def get_value(self):
                return self.get_inner_value()

            @self.tape_recorder.intercept_input('inner_input')
            def get_inner_value(self):
                return self.seed

        instance = Operation(5)
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())
        self._assert_playback_vs_recording(playback_result, result)

        # Check the recording it self does not contain the inner interception
        recording = self.tape_cassette.get_recording(recording_id)
        self.assertIsNone(next((k for k in recording.get_all_keys() if 'inner_input' in k), None))

    def test_record_and_playback_basic_operation_new_output_added_post_recording(self):
        class OperationOld(object):

            @self.tape_recorder.operation()
            def execute(self):
                return self.output(5)

            @self.tape_recorder.intercept_output('output_function')
            def output(self, value):
                return value

        class OperationNew(object):

            @self.tape_recorder.operation()
            def execute(self):
                value = self.output(5)
                self.output_new(value)
                return value

            @self.tape_recorder.intercept_output('output_function')
            def output(self, value):
                return value

            @self.tape_recorder.intercept_output('output_new_function', fail_on_no_recorded_result=False)
            def output_new(self, value):
                return value

        instance = OperationOld()
        result = instance.execute()
        self.assertEqual(5, result)

        recording_id = self.tape_cassette.get_last_recording_id()
        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: OperationNew().execute())

        self.assertEqual({'args': [5], 'kwargs': {}}, playback_result.playback_outputs[0].value)
        self.assertEqual({'args': [5], 'kwargs': {}}, playback_result.playback_outputs[1].value)
        self.assertNotEqual(playback_result.playback_outputs[0].key, playback_result.playback_outputs[1].key)
        self.assertIn('output_function', playback_result.playback_outputs[0].key)
        self.assertIn('output_new_function', playback_result.playback_outputs[1].key)

    def test_current_recording_id(self):
        tape_recorder = self.tape_recorder

        class Operation(object):

            @self.tape_recorder.operation()
            def execute(self):
                return tape_recorder.current_recording_id

        instance = Operation()
        result = instance.execute()
        recording_id = self.tape_cassette.get_last_recording_id()
        self.assertEqual(recording_id, result)

        playback_result = self.tape_recorder.play(recording_id,
                                                  playback_function=lambda recording: Operation().execute())

        operation_output = next(po for po in playback_result.playback_outputs
                                if TapeRecorder.OPERATION_OUTPUT_ALIAS in po.key)
        self.assertEqual(recording_id, operation_output.value['args'][0])

        self.tape_recorder.disable_recording()
        instance = Operation()
        self.assertIsNone(instance.execute())
Пример #3
0
class TestPlaybackStudio(unittest.TestCase):
    def setUp(self):
        self.tape_cassette = InMemoryTapeCassette()
        self.tape_recorder = TapeRecorder(self.tape_cassette)
        self.tape_recorder.enable_recording()

    def tearDown(self):
        self.tape_cassette.close()

    def test_run_two_categories(self):
        self._test_run_two_operations(use_recording_ids=False)

    def test_run_specific_recording_ids(self):
        self._test_run_two_operations(use_recording_ids=True)

    def _test_run_two_operations(self, use_recording_ids):
        class A(object):
            @self.tape_recorder.operation()
            def execute(self):
                return 'AAA'

        class B(object):
            @self.tape_recorder.operation()
            def execute(self):
                return 'BBB'

        A().execute()
        A().execute()
        B().execute()

        if not use_recording_ids:
            categories = ['A', 'B', 'C']
            recording_ids = None
        else:
            categories = None
            recording_ids = self.tape_cassette.get_all_recording_ids()

        class MockEqualizerTuner(EqualizerTuner):
            def create_category_tuning(self, category):
                if category not in ['A', 'B']:
                    raise Exception("Unsupported")

                def result_extractor(outputs):
                    return next(
                        o.value['args'][0] for o in outputs
                        if TapeRecorder.OPERATION_OUTPUT_ALIAS in o.key)

                def comparator(expected, actual, message):
                    return ComparatorResult(
                        EqualityStatus.Equal if expected == actual else
                        EqualityStatus.Different, message)

                def comparison_data_extractor(recording):
                    return {'message': category}

                def playback_function(recording):
                    cls_name = recording.get_metadata()[
                        TapeRecorder.OPERATION_CLASS]
                    if 'studio.A' in list(cls_name.values())[0]:
                        return A().execute()
                    return B().execute()

                return EqualizerTuning(
                    playback_function=playback_function,
                    result_extractor=result_extractor,
                    comparator=comparator,
                    comparison_data_extractor=comparison_data_extractor)

        equalizer_tuner = MockEqualizerTuner()
        start_date = 'a'
        studio = PlaybackStudio(
            categories,
            equalizer_tuner,
            self.tape_recorder,
            lookup_properties=RecordingLookupProperties(start_date),
            recording_ids=recording_ids,
            compare_execution_config=CompareExecutionConfig(
                keep_results_in_comparison=True))
        result = studio.play()

        a_results = list(result['A'])
        b_results = list(result['B'])

        self.assertEqual(2, len(a_results))
        self.assertTrue(all('AAA' == result.actual for result in a_results))
        self.assertTrue(all('AAA' == result.expected for result in a_results))
        self.assertTrue(
            all(EqualityStatus.Equal ==
                result.comparator_status.equality_status
                for result in a_results))
        self.assertTrue(
            all('A' == result.comparator_status.message
                for result in a_results))

        self.assertEqual(1, len(b_results))
        self.assertTrue(all('BBB' == result.actual for result in b_results))
        self.assertTrue(all('BBB' == result.expected for result in b_results))
        self.assertTrue(
            all(EqualityStatus.Equal ==
                result.comparator_status.equality_status
                for result in b_results))
        self.assertTrue(
            all('B' == result.comparator_status.message
                for result in b_results))
Пример #4
0
class TestEqualizer(unittest.TestCase):

    def setUp(self):
        self.tape_cassette = InMemoryTapeCassette()
        self.tape_recorder = TapeRecorder(self.tape_cassette)
        self.tape_recorder.enable_recording()

    def tearDown(self):
        self.tape_cassette.close()

    @parameterized.expand([("compare_in_dedicated_process", True),
                           ("playback_same_process", False),
                           ])
    def test_equal_comparison(self, name, compare_in_dedicated_process):

        class Operation(object):
            def __init__(self, value=None, multiply_input=1):
                self._value = value
                self.multiply_input = multiply_input

            @property
            @self.tape_recorder.intercept_input('input')
            def input(self):
                return self._value

            @self.tape_recorder.operation()
            def execute(self):
                return self.input * self.multiply_input

        Operation(3).execute()
        Operation(4).execute()
        Operation(5).execute()

        playback_counter = [0]

        def playback_function(recording):
            playback_counter[0] += 1
            if playback_counter[0] == 2:
                operation = Operation(multiply_input=2)
            elif playback_counter[0] == 3:
                operation = Operation(multiply_input=100)
            else:
                operation = Operation()
            return operation.execute()

        def player(recording_id):
            return self.tape_recorder.play(recording_id, playback_function)

        with patch.object(InMemoryTapeCassette, 'iter_recording_ids',
                          wraps=self.tape_cassette.iter_recording_ids) as mock:
            start_date = datetime.utcnow() - timedelta(hours=1)
            end_date = datetime.utcnow() + timedelta(hours=1)
            playable_recordings = find_matching_recording_ids(
                self.tape_recorder,
                category=Operation.__name__,
                lookup_properties=RecordingLookupProperties(start_date=start_date, end_date=end_date),
            )

            runner = Equalizer(playable_recordings, player, result_extractor=return_value_result_extractor,
                               comparator=exact_comparator, 
                               compare_execution_config=CompareExecutionConfig(
                                   keep_results_in_comparison=True,
                                   compare_in_dedicated_process=compare_in_dedicated_process
                               ))

            with patch.object(Equalizer, '_play_and_compare_recording',
                              wraps=runner._play_and_compare_recording) as wrapped:
                comparison = list(runner.run_comparison())
            if compare_in_dedicated_process:
                wrapped.assert_not_called()
            else:
                wrapped.assert_called()

        self.assertEqual(EqualityStatus.Equal, comparison[0].comparator_status.equality_status)
        self.assertEqual(EqualityStatus.Different, comparison[1].comparator_status.equality_status)
        self.assertEqual(EqualityStatus.Failed, comparison[2].comparator_status.equality_status)

        self.assertEqual(3, comparison[0].expected)
        self.assertEqual(4, comparison[1].expected)
        self.assertEqual(5, comparison[2].expected)

        self.assertEqual(3, comparison[0].actual)
        self.assertEqual(8, comparison[1].actual)
        self.assertEqual(500, comparison[2].actual)

        mock.assert_called_with(Operation.__name__, start_date=start_date, end_date=end_date, limit=None, metadata=None)
        self.assertGreaterEqual(comparison[0].playback.playback_duration, 0)
        self.assertGreaterEqual(comparison[0].playback.recorded_duration, 0)

    @parameterized.expand([("compare_in_dedicated_process", True),
                           ("playback_same_process", False),
                           ])
    def test_with_exception_comparison(self, name, compare_in_dedicated_process):

            class Operation(object):
                def __init__(self, value=None, raise_error=None):
                    self._value = value
                    self.raise_error = raise_error

                @property
                @self.tape_recorder.intercept_input('input')
                def input(self):
                    return self._value

                @self.tape_recorder.operation()
                def execute(self, value=None):
                    if self.raise_error:
                        raise Exception("error")
                    if value is not None:
                        return value
                    return self.input

            Operation(3).execute()
            Operation(4).execute()
            with suppress(Exception):
                Operation(raise_error=True).execute()

            playback_counter = [0]

            def playback_function(recording):
                playback_counter[0] += 1
                if playback_counter[0] == 2:
                    operation = Operation(raise_error=True)
                elif playback_counter[0] == 3:
                    return Operation().execute(5)
                else:
                    operation = Operation()
                return operation.execute()

            def player(recording_id):
                return self.tape_recorder.play(recording_id, playback_function)

            with patch.object(InMemoryTapeCassette, 'iter_recording_ids',
                              wraps=self.tape_cassette.iter_recording_ids):
                start_date = datetime.utcnow() - timedelta(hours=1)
                playable_recordings = find_matching_recording_ids(
                    self.tape_recorder,
                    category=Operation.__name__,
                    lookup_properties=RecordingLookupProperties(start_date=start_date),
                )
                runner = Equalizer(playable_recordings, player, result_extractor=return_value_result_extractor,
                                   comparator=exact_comparator, compare_execution_config=CompareExecutionConfig(
                                       keep_results_in_comparison=True,
                                       compare_in_dedicated_process=compare_in_dedicated_process
                                   ))

                comparison = list(runner.run_comparison())

            self.assertEqual(EqualityStatus.Equal, comparison[0].comparator_status.equality_status)
            self.assertEqual(EqualityStatus.Different, comparison[1].comparator_status.equality_status)
            self.assertEqual(EqualityStatus.Different, comparison[2].comparator_status.equality_status)

            self.assertEqual(3, comparison[0].expected)
            self.assertEqual(4, comparison[1].expected)
            self.assertIsInstance(comparison[2].expected, Exception)
            self.assertTrue(comparison[1].actual_is_exception)
            self.assertFalse(comparison[1].expected_is_exception)

            self.assertEqual(3, comparison[0].actual)
            self.assertIsInstance(comparison[1].actual, Exception)
            self.assertEqual(5, comparison[2].actual)
            self.assertFalse(comparison[2].actual_is_exception)
            self.assertTrue(comparison[2].expected_is_exception)

    @parameterized.expand([("compare_in_dedicated_process", True),
                           ("playback_same_process", False),
                           ])
    def test_comparison_with_meta_and_limit_filtering(self, name, compare_in_dedicated_process):

        class Operation(object):
            def __init__(self, value=None):
                self._value = value

            @property
            @self.tape_recorder.intercept_input('input')
            def input(self):
                return self._value

            @self.tape_recorder.operation(metadata_extractor=lambda op_self, meta=None: {'meta': meta})
            def execute(self, meta=None):
                return self.input

        Operation(3).execute('a')
        Operation(4).execute('b')
        Operation(5).execute('b')

        def playback_function(recording):
            return Operation().execute()

        def player(recording_id):
            return self.tape_recorder.play(recording_id, playback_function)

        with patch.object(InMemoryTapeCassette, 'iter_recording_ids',
                          wraps=self.tape_cassette.iter_recording_ids) as mock:
            start_date = datetime.utcnow() - timedelta(hours=1)
            playable_recordings = find_matching_recording_ids(
                self.tape_recorder,
                category=Operation.__name__,
                lookup_properties=RecordingLookupProperties(
                    start_date=start_date,
                    metadata={'meta': 'b'},
                    limit=1),
            )
            runner = Equalizer(playable_recordings, player, result_extractor=return_value_result_extractor,
                               comparator=exact_comparator,
                               compare_execution_config=CompareExecutionConfig(
                                   keep_results_in_comparison=True,
                                   compare_in_dedicated_process=compare_in_dedicated_process
                               ))

            comparison = list(runner.run_comparison())

        self.assertEqual(1, len(comparison))
        self.assertEqual(EqualityStatus.Equal, comparison[0].comparator_status.equality_status)

        mock.assert_called_with(Operation.__name__, start_date=start_date, end_date=None,
                                limit=1, metadata={'meta': 'b'})
        self.assertEqual(4, comparison[0].expected)
        self.assertEqual(4, comparison[0].actual)

    @parameterized.expand([("compare_in_dedicated_process", True),
                           ("playback_same_process", False),
                           ])
    def test_equal_comparison_with_message(self, name, compare_in_dedicated_process):

        class Operation(object):
            def __init__(self, value=None, multiply_input=1):
                self._value = value
                self.multiply_input = multiply_input

            @property
            @self.tape_recorder.intercept_input('input')
            def input(self):
                return self._value

            @self.tape_recorder.operation()
            def execute(self):
                return self.input * self.multiply_input

        Operation(3).execute()
        Operation(4).execute()
        Operation(5).execute()

        playback_counter = [0]

        def playback_function(recording):
            playback_counter[0] += 1
            if playback_counter[0] == 2:
                operation = Operation(multiply_input=2)
            elif playback_counter[0] == 3:
                operation = Operation(multiply_input=100)
            else:
                operation = Operation()
            return operation.execute()

        def player(recording_id):
            return self.tape_recorder.play(recording_id, playback_function)

        with patch.object(InMemoryTapeCassette, 'iter_recording_ids',
                          wraps=self.tape_cassette.iter_recording_ids) as mock:
            start_date = datetime.utcnow() - timedelta(hours=1)
            end_date = datetime.utcnow() + timedelta(hours=1)
            playable_recordings = find_matching_recording_ids(
                self.tape_recorder,
                category=Operation.__name__,
                lookup_properties=RecordingLookupProperties(
                    start_date=start_date,
                    end_date=end_date)
            )
            runner = Equalizer(playable_recordings, player, result_extractor=return_value_result_extractor,
                               comparator=exact_comparator_with_message,
                               compare_execution_config=CompareExecutionConfig(
                                   keep_results_in_comparison=True,
                                   compare_in_dedicated_process=compare_in_dedicated_process
                               ))

            comparison = list(runner.run_comparison())

        self.assertEqual(EqualityStatus.Equal, comparison[0].comparator_status.equality_status)
        self.assertEqual(EqualityStatus.Different, comparison[1].comparator_status.equality_status)
        self.assertEqual(EqualityStatus.Failed, comparison[2].comparator_status.equality_status)

        self.assertEqual(EqualityStatus.Equal.name, comparison[0].comparator_status.message)
        self.assertEqual(EqualityStatus.Different.name, comparison[1].comparator_status.message)
        self.assertEqual(EqualityStatus.Failed.name, comparison[2].comparator_status.message)

    @parameterized.expand([("compare_in_dedicated_process", True),
                           ("playback_same_process", False),
                           ])
    def test_equal_comparison_comparator_data_extraction(self, name, compare_in_dedicated_process):

        class Operation(object):
            def __init__(self, value=None, override_input=None):
                self._value = value
                self.override_input = override_input

            @property
            @self.tape_recorder.intercept_input('input')
            def input(self):
                return self._value

            @self.tape_recorder.operation()
            def execute(self):
                if self.override_input:
                    return self.override_input
                return self.input

        Operation(3).execute()
        Operation(4).execute()
        Operation(5).execute()

        playback_counter = [0]

        def playback_function(recording):
            playback_counter[0] += 1
            if playback_counter[0] == 2:
                operation = Operation(override_input=100)
            else:
                operation = Operation()
            return operation.execute()

        def player(recording_id):
            return self.tape_recorder.play(recording_id, playback_function)

        def comparison_data_extractor(recording):
            self.assertIsNotNone(recording)
            return {'multiplier': 1}

        def exact_comparator_with_multiplier(recorded_result, playback_result, multiplier):
            if playback_result >= 10:
                playback_result *= 0
                recorded_result *= 0
            else:
                playback_result *= multiplier
                recorded_result *= multiplier

            return ComparatorResult(
                EqualityStatus.Equal if recorded_result * multiplier == playback_result * multiplier
                else EqualityStatus.Different, str(multiplier))

        with patch.object(InMemoryTapeCassette, 'iter_recording_ids',
                          wraps=self.tape_cassette.iter_recording_ids) as mock:
            start_date = datetime.utcnow() - timedelta(hours=1)
            end_date = datetime.utcnow() + timedelta(hours=1)
            playable_recordings = find_matching_recording_ids(
                self.tape_recorder,
                category=Operation.__name__,
                lookup_properties=RecordingLookupProperties(
                    start_date=start_date,
                    end_date=end_date)
            )
            runner = Equalizer(playable_recordings, player, result_extractor=return_value_result_extractor,
                               comparator=exact_comparator_with_multiplier,
                               comparison_data_extractor=comparison_data_extractor,
                               compare_execution_config=CompareExecutionConfig(
                                   keep_results_in_comparison=True,
                                   compare_in_dedicated_process=compare_in_dedicated_process
                               ))

            comparison = list(runner.run_comparison())

        self.assertEqual(EqualityStatus.Equal, comparison[0].comparator_status.equality_status)
        self.assertEqual(EqualityStatus.Equal, comparison[1].comparator_status.equality_status)
        self.assertEqual(EqualityStatus.Equal, comparison[2].comparator_status.equality_status)

        self.assertEqual('1', comparison[0].comparator_status.message)
        self.assertEqual('1', comparison[1].comparator_status.message)
        self.assertEqual('1', comparison[2].comparator_status.message)

    def test_random_sample(self):

        class Operation(object):
            def __init__(self, value=None):
                self._value = value

            @property
            @self.tape_recorder.intercept_input('input')
            def input(self):
                return self._value

            @self.tape_recorder.operation()
            def execute(self):
                return self.input

        for i in range(10):
            Operation(i).execute()

        def playback_function(recording):
            return Operation().execute()

        random.seed(12)
        first_list = None
        with patch.object(InMemoryTapeCassette, 'iter_recording_ids',
                          wraps=self.tape_cassette.iter_recording_ids) as mock:
            start_date = datetime.utcnow() - timedelta(hours=1)
            playable_recordings = find_matching_recording_ids(
                self.tape_recorder,
                category=Operation.__name__,
                lookup_properties=RecordingLookupProperties(
                    start_date=start_date,
                    random_sample=True,
                    limit=3)
            )
            first_list = list(playable_recordings)
            self.assertEqual(len(first_list), 3)

        random.seed(4)
        with patch.object(InMemoryTapeCassette, 'iter_recording_ids',
                          wraps=self.tape_cassette.iter_recording_ids) as mock:
            start_date = datetime.utcnow() - timedelta(hours=1)
            playable_recordings = find_matching_recording_ids(
                self.tape_recorder,
                category=Operation.__name__,
                lookup_properties=RecordingLookupProperties(
                    start_date=start_date,
                    random_sample=True,
                    limit=3)
            )
            second_list = list(playable_recordings)
            self.assertNotEqual(second_list, first_list)

    @parameterized.expand([("compare_in_dedicated_process", True),
                           ("playback_same_process", False),
                           ])
    def test_run_with_specific_ids(self, name, compare_in_dedicated_process):

        class Operation(object):
            def __init__(self, value=None, override_input=None):
                self._value = value
                self.override_input = override_input

            @property
            @self.tape_recorder.intercept_input('input')
            def input(self):
                return self._value

            @self.tape_recorder.operation()
            def execute(self):
                if self.override_input:
                    return self.override_input
                return self.input

        Operation(3).execute()
        id1 = self.tape_cassette.get_last_recording_id()
        Operation(4).execute()
        Operation(5).execute()
        id2 = self.tape_cassette.get_last_recording_id()
        Operation(6).execute()

        playback_counter = [0]

        def playback_function(recording):
            playback_counter[0] += 1
            if playback_counter[0] == 2:
                operation = Operation(override_input=100)
            else:
                operation = Operation()
            return operation.execute()

        def player(recording_id):
            return self.tape_recorder.play(recording_id, playback_function)

        runner = Equalizer([id1, id2], player, result_extractor=return_value_result_extractor,
                           comparator=exact_comparator,
                           compare_execution_config=CompareExecutionConfig(
                               keep_results_in_comparison=True,
                               compare_in_dedicated_process=compare_in_dedicated_process
                           ))

        comparison = list(runner.run_comparison())

        self.assertEqual(id1, comparison[0].playback.original_recording.id)
        self.assertEqual(id2, comparison[1].playback.original_recording.id)
        self.assertEqual(2, len(comparison))

    def test_equalizer_recycle_process_thread(self):
        class Operation(object):
            def __init__(self, value=None):
                self._value = value

            @property
            @self.tape_recorder.intercept_input('input')
            def input(self):
                return self._value

            @self.tape_recorder.operation()
            def execute(self):
                return self.input

        for i in range(20):
            Operation(i).execute()

        def playback_function(recording):
            return Operation().execute()

        def player(recording_id):
            return self.tape_recorder.play(recording_id, playback_function)

        with patch.object(InMemoryTapeCassette, 'iter_recording_ids',
                          wraps=self.tape_cassette.iter_recording_ids) as mock:
            start_date = datetime.utcnow() - timedelta(hours=1)
            end_date = datetime.utcnow() + timedelta(hours=1)
            playable_recordings = find_matching_recording_ids(
                self.tape_recorder,
                category=Operation.__name__,
                lookup_properties=RecordingLookupProperties(start_date=start_date, end_date=end_date),
            )

            runner = Equalizer(playable_recordings, player, result_extractor=return_value_result_extractor,
                               comparator=exact_comparator,
                               compare_execution_config=CompareExecutionConfig(
                                   keep_results_in_comparison=True,
                                   compare_in_dedicated_process=True,
                                   compare_process_recycle_rate=2
                               ))

            with patch.object(Equalizer, '_create_new_player_process',
                              wraps=runner._create_new_player_process) as wrapped:
                comparison = list(runner.run_comparison())

            self.assertEqual(10, wrapped.call_count)

        self.assertEqual(20, len(comparison))
        for c in comparison:
            self.assertEqual(EqualityStatus.Equal, c.comparator_status.equality_status)
Пример #5
0
# We create the tape recorder singleton that will be used by the endpoints
import os

from playback.tape_cassettes.asynchronous.async_record_only_tape_cassette import AsyncRecordOnlyTapeCassette
from playback.tape_cassettes.file_based.file_based_tape_cassette import FileBasedTapeCassette
from playback.tape_recorder import TapeRecorder

tape_recorder = TapeRecorder(None)
tape_recorder.disable_recording()

# For demonstration purpose, persist file in underline recordings dir
recordings_path = os.path.dirname(os.path.realpath(__file__)) + '/recordings'


def init_recording_mode():
    """
    Initialize and start the tape cassette and tape recorder for recording mode.
    in order for the recording not to be blocking and part of the operation latency, we wrap it with the asynchronous
    tape cassette which will flush the recording to a file in a separate thread, reducing the impact on the thread
    that executes the operation
    """
    print("Starting recording mode - recordings will be saved to {}".format(
        recordings_path))
    tape_cassette = AsyncRecordOnlyTapeCassette(
        FileBasedTapeCassette(recordings_path))
    tape_cassette.start()
    tape_recorder.tape_cassette = tape_cassette
    # This will activate the tape recorder in recording mode
    tape_recorder.enable_recording()