# Copyright 2013 Red Hat, Inc.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import sys

import fixtures
import oslo_messaging as messaging


class CastAsCallFixture(fixtures.Fixture):

    """Make RPC 'cast' behave like a 'call'.

    This is a little hack for tests that need to know when a cast
    operation has completed. The idea is that we wait for the RPC
    endpoint method to complete and return before continuing on the
    caller.

    See Ia7f40718533e450f00cd3e7d753ac65755c70588 for more background.
    """

    def __init__(self, testcase):
        super().__init__()
        self.testcase = testcase

    @staticmethod
    def _stub_out(testcase, obj=None):
        if obj:
            orig_prepare = obj.prepare
        else:
            orig_prepare = messaging.RPCClient.prepare

        def prepare(self, *args, **kwargs):
            # Casts with fanout=True would throw errors if its monkeypatched to
            # the call method, so we must override fanout to False
            if 'fanout' in kwargs:
                kwargs['fanout'] = False
            cctxt = orig_prepare(self, *args, **kwargs)
            CastAsCallFixture._stub_out(testcase, cctxt)  # woo, recurse!
            return cctxt

        if obj:
            cls = getattr(sys.modules[obj.__class__.__module__],
                          obj.__class__.__name__)
            testcase.stub_out('%s.%s.prepare' % (obj.__class__.__module__,
                                                 obj.__class__.__name__),
                              prepare)
            testcase.stub_out('%s.%s.cast' % (obj.__class__.__module__,
                                              obj.__class__.__name__),
                              cls.call)
        else:
            testcase.stub_out('oslo_messaging.RPCClient.prepare', prepare)
            testcase.stub_out('oslo_messaging.RPCClient.cast',
                              messaging.RPCClient.call)

    def setUp(self):
        super().setUp()
        self._stub_out(self.testcase)
