15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/python2.4
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright 2008 Google Inc.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Licensed under the Apache License, Version 2.0 (the "License");
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# you may not use this file except in compliance with the License.
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# You may obtain a copy of the License at
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#      http://www.apache.org/licenses/LICENSE-2.0
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Unless required by applicable law or agreed to in writing, software
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# distributed under the License is distributed on an "AS IS" BASIS,
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# See the License for the specific language governing permissions and
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# limitations under the License.
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# This file is used for testing.  The original is at:
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   http://code.google.com/p/pymox/
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Mox, an object-mocking framework for Python.
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Mox works in the record-replay-verify paradigm.  When you first create
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)a mock object, it is in record mode.  You then programmatically set
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)the expected behavior of the mock object (what methods are to be
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)called on it, with what parameters, what they should return, and in
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)what order).
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Once you have set up the expected mock behavior, you put it in replay
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)mode.  Now the mock responds to method calls just as you told it to.
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)If an unexpected method (or an expected method with unexpected
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)parameters) is called, then an exception will be raised.
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Once you are done interacting with the mock, you need to verify that
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)all the expected interactions occured.  (Maybe your code exited
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)prematurely without calling some cleanup method!)  The verify phase
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ensures that every expected method was called; otherwise, an exception
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)will be raised.
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Suggested usage / workflow:
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Create Mox factory
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my_mox = Mox()
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Create a mock data access object
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mock_dao = my_mox.CreateMock(DAOClass)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Set up expected behavior
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mock_dao.RetrievePersonWithIdentifier('1').AndReturn(person)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mock_dao.DeletePerson(person)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Put mocks in replay mode
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my_mox.ReplayAll()
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Inject mock object and run test
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  controller.SetDao(mock_dao)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  controller.DeletePersonById('1')
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Verify all methods were called as expected
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my_mox.VerifyAll()
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from collections import deque
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import types
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import unittest
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import stubout
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Error(AssertionError):
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Base exception for this module."""
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pass
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ExpectedMethodCallsError(Error):
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Raised when Verify() is called before all expected methods have been called
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, expected_methods):
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Init exception.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # expected_methods: A sequence of MockMethod objects that should have been
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #   called.
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expected_methods: [MockMethod]
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ValueError: if expected_methods contains no methods.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not expected_methods:
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise ValueError("There must be at least one expected method")
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Error.__init__(self)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._expected_methods = expected_methods
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __str__(self):
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    calls = "\n".join(["%3d.  %s" % (i, m)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       for i, m in enumerate(self._expected_methods)])
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return "Verify: Expected methods never called:\n%s" % (calls,)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class UnexpectedMethodCallError(Error):
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Raised when an unexpected method is called.
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This can occur if a method is called with incorrect parameters, or out of the
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  specified order.
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, unexpected_method, expected):
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Init exception.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # unexpected_method: MockMethod that was called but was not at the head of
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #   the expected_method queue.
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # expected: MockMethod or UnorderedGroup the method should have
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #   been in.
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unexpected_method: MockMethod
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expected: MockMethod or UnorderedGroup
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Error.__init__(self)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._unexpected_method = unexpected_method
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._expected = expected
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __str__(self):
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return "Unexpected method call: %s.  Expecting: %s" % \
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (self._unexpected_method, self._expected)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class UnknownMethodCallError(Error):
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Raised if an unknown method is requested of the mock object."""
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, unknown_method_name):
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Init exception.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # unknown_method_name: Method call that is not part of the mocked class's
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #   public interface.
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unknown_method_name: str
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Error.__init__(self)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._unknown_method_name = unknown_method_name
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __str__(self):
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return "Method called is not a member of the object: %s" % \
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._unknown_method_name
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Mox(object):
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Mox: a factory for creating mock objects."""
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # A list of types that should be stubbed out with MockObjects (as
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # opposed to MockAnythings).
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  _USE_MOCK_OBJECT = [types.ClassType, types.InstanceType, types.ModuleType,
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      types.ObjectType, types.TypeType]
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self):
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initialize a new Mox."""
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._mock_objects = []
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.stubs = stubout.StubOutForTesting()
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CreateMock(self, class_to_mock):
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Create a new mock object.
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # class_to_mock: the class to be mocked
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      class_to_mock: class
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      MockObject that can be used as the class_to_mock would be.
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_mock = MockObject(class_to_mock)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._mock_objects.append(new_mock)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return new_mock
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CreateMockAnything(self):
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Create a mock that will accept any method calls.
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This does not enforce an interface.
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_mock = MockAnything()
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._mock_objects.append(new_mock)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return new_mock
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ReplayAll(self):
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Set all mock objects to replay mode."""
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for mock_obj in self._mock_objects:
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mock_obj._Replay()
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def VerifyAll(self):
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Call verify on all mock objects created."""
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for mock_obj in self._mock_objects:
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mock_obj._Verify()
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ResetAll(self):
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Call reset on all mock objects.  This does not unset stubs."""
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for mock_obj in self._mock_objects:
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mock_obj._Reset()
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def StubOutWithMock(self, obj, attr_name, use_mock_anything=False):
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Replace a method, attribute, etc. with a Mock.
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This will replace a class or module with a MockObject, and everything else
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (method, function, etc) with a MockAnything.  This can be overridden to
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    always use a MockAnything by setting use_mock_anything to True.
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      obj: A Python object (class, module, instance, callable).
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      attr_name: str.  The name of the attribute to replace with a mock.
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      use_mock_anything: bool. True if a MockAnything should be used regardless
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        of the type of attribute.
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    attr_to_replace = getattr(obj, attr_name)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if type(attr_to_replace) in self._USE_MOCK_OBJECT and not use_mock_anything:
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      stub = self.CreateMock(attr_to_replace)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      stub = self.CreateMockAnything()
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.stubs.Set(obj, attr_name, stub)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def UnsetStubs(self):
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Restore stubs to their original state."""
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.stubs.UnsetAll()
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Replay(*args):
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Put mocks into Replay mode.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # args is any number of mocks to put into replay mode.
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for mock in args:
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    mock._Replay()
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Verify(*args):
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Verify mocks.
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # args is any number of mocks to be verified.
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for mock in args:
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    mock._Verify()
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Reset(*args):
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Reset mocks.
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # args is any number of mocks to be reset.
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for mock in args:
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    mock._Reset()
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MockAnything:
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A mock that can be used to mock anything.
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This is helpful for mocking classes that do not provide a public interface.
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self):
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """ """
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._Reset()
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __getattr__(self, method_name):
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Intercept method calls on this object.
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     A new MockMethod is returned that is aware of the MockAnything's
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     state (record or replay).  The call will be recorded or replayed
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     by the MockMethod's __call__.
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # method name: the name of the method being called.
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      method_name: str
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      A new MockMethod aware of MockAnything's state (record or replay).
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._CreateMockMethod(method_name)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _CreateMockMethod(self, method_name):
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Create a new mock method call and return it.
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # method name: the name of the method being called.
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      method_name: str
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      A new MockMethod aware of MockAnything's state (record or replay).
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return MockMethod(method_name, self._expected_calls_queue,
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      self._replay_mode)
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __nonzero__(self):
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Return 1 for nonzero so the mock can be used as a conditional."""
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __eq__(self, rhs):
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Provide custom logic to compare objects."""
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return (isinstance(rhs, MockAnything) and
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._replay_mode == rhs._replay_mode and
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._expected_calls_queue == rhs._expected_calls_queue)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __ne__(self, rhs):
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Provide custom logic to compare objects."""
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return not self == rhs
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _Replay(self):
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Start replaying expected method calls."""
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._replay_mode = True
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _Verify(self):
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Verify that all of the expected calls have been made.
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ExpectedMethodCallsError: if there are still more method calls in the
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        expected queue.
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If the list of expected calls is not empty, raise an exception
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._expected_calls_queue:
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # The last MultipleTimesGroup is not popped from the queue.
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (len(self._expected_calls_queue) == 1 and
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          isinstance(self._expected_calls_queue[0], MultipleTimesGroup) and
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._expected_calls_queue[0].IsSatisfied()):
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        pass
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise ExpectedMethodCallsError(self._expected_calls_queue)
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _Reset(self):
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Reset the state of this mock to record mode with an empty queue."""
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Maintain a list of method calls we are expecting
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._expected_calls_queue = deque()
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Make sure we are in setup mode, not replay mode
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._replay_mode = False
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MockObject(MockAnything, object):
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A mock object that simulates the public/protected interface of a class."""
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, class_to_mock):
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initialize a mock object.
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This determines the methods and properties of the class and stores them.
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # class_to_mock: class to be mocked
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      class_to_mock: class
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # This is used to hack around the mixin/inheritance of MockAnything, which
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # is not a proper object (it can be anything. :-)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MockAnything.__dict__['__init__'](self)
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Get a list of all the public and special methods we should mock.
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._known_methods = set()
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._known_vars = set()
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._class_to_mock = class_to_mock
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for method in dir(class_to_mock):
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if callable(getattr(class_to_mock, method)):
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._known_methods.add(method)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._known_vars.add(method)
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __getattr__(self, name):
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Intercept attribute request on this object.
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    If the attribute is a public class variable, it will be returned and not
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    recorded as a call.
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    If the attribute is not a variable, it is handled like a method
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    call. The method name is checked against the set of mockable
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    methods, and a new MockMethod is returned that is aware of the
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MockObject's state (record or replay).  The call will be recorded
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    or replayed by the MockMethod's __call__.
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # name: the name of the attribute being requested.
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name: str
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Either a class variable or a new MockMethod that is aware of the state
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      of the mock (record or replay).
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UnknownMethodCallError if the MockObject does not mock the requested
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          method.
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if name in self._known_vars:
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return getattr(self._class_to_mock, name)
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if name in self._known_methods:
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return self._CreateMockMethod(name)
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise UnknownMethodCallError(name)
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __eq__(self, rhs):
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Provide custom logic to compare objects."""
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return (isinstance(rhs, MockObject) and
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._class_to_mock == rhs._class_to_mock and
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._replay_mode == rhs._replay_mode and
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._expected_calls_queue == rhs._expected_calls_queue)
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __setitem__(self, key, value):
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Provide custom logic for mocking classes that support item assignment.
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      key: Key to set the value for.
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      value: Value to set.
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Expected return value in replay mode.  A MockMethod object for the
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      __setitem__ method that has already been called if not in replay mode.
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      TypeError if the underlying class does not support item assignment.
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UnexpectedMethodCallError if the object does not expect the call to
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        __setitem__.
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    setitem = self._class_to_mock.__dict__.get('__setitem__', None)
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Verify the class supports item assignment.
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if setitem is None:
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise TypeError('object does not support item assignment')
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If we are in replay mode then simply call the mock __setitem__ method.
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._replay_mode:
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return MockMethod('__setitem__', self._expected_calls_queue,
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        self._replay_mode)(key, value)
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Otherwise, create a mock method __setitem__.
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._CreateMockMethod('__setitem__')(key, value)
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __getitem__(self, key):
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Provide custom logic for mocking classes that are subscriptable.
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      key: Key to return the value for.
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Expected return value in replay mode.  A MockMethod object for the
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      __getitem__ method that has already been called if not in replay mode.
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      TypeError if the underlying class is not subscriptable.
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UnexpectedMethodCallError if the object does not expect the call to
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        __setitem__.
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getitem = self._class_to_mock.__dict__.get('__getitem__', None)
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Verify the class supports item assignment.
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if getitem is None:
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise TypeError('unsubscriptable object')
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If we are in replay mode then simply call the mock __getitem__ method.
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._replay_mode:
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return MockMethod('__getitem__', self._expected_calls_queue,
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        self._replay_mode)(key)
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Otherwise, create a mock method __getitem__.
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._CreateMockMethod('__getitem__')(key)
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __call__(self, *params, **named_params):
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Provide custom logic for mocking classes that are callable."""
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Verify the class we are mocking is callable
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    callable = self._class_to_mock.__dict__.get('__call__', None)
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if callable is None:
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise TypeError('Not callable')
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Because the call is happening directly on this object instead of a method,
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # the call on the mock method is made right here
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    mock_method = self._CreateMockMethod('__call__')
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return mock_method(*params, **named_params)
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  @property
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __class__(self):
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Return the class that is being mocked."""
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._class_to_mock
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MockMethod(object):
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Callable mock method.
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  A MockMethod should act exactly like the method it mocks, accepting parameters
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  and returning a value, or throwing an exception (as specified).  When this
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  method is called, it can optionally verify whether the called method (name and
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  signature) matches the expected method.
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, method_name, call_queue, replay_mode):
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Construct a new mock method.
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # method_name: the name of the method
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # call_queue: deque of calls, verify this call against the head, or add
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #     this call to the queue.
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # replay_mode: False if we are recording, True if we are verifying calls
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #     against the call queue.
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      method_name: str
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      call_queue: list or deque
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      replay_mode: bool
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._name = method_name
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._call_queue = call_queue
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not isinstance(call_queue, deque):
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._call_queue = deque(self._call_queue)
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._replay_mode = replay_mode
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._params = None
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._named_params = None
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._return_value = None
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._exception = None
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._side_effects = None
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __call__(self, *params, **named_params):
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Log parameters and return the specified return value.
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    If the Mock(Anything/Object) associated with this call is in record mode,
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this MockMethod will be pushed onto the expected call queue.  If the mock
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    is in replay mode, this will pop a MockMethod off the top of the queue and
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    verify this call is equal to the expected call.
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UnexpectedMethodCall if this call is supposed to match an expected method
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        call and it does not.
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._params = params
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._named_params = named_params
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._replay_mode:
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._call_queue.append(self)
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return self
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expected_method = self._VerifyMethodCall()
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if expected_method._side_effects:
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expected_method._side_effects(*params, **named_params)
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if expected_method._exception:
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise expected_method._exception
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return expected_method._return_value
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __getattr__(self, name):
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Raise an AttributeError with a helpful message."""
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise AttributeError('MockMethod has no attribute "%s". '
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'Did you remember to put your mocks in replay mode?' % name)
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _PopNextMethod(self):
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Pop the next method from our call queue."""
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return self._call_queue.popleft()
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except IndexError:
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise UnexpectedMethodCallError(self, None)
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _VerifyMethodCall(self):
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Verify the called method is expected.
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This can be an ordered method, or part of an unordered set.
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The expected mock method.
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UnexpectedMethodCall if the method called was not expected.
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expected = self._PopNextMethod()
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Loop here, because we might have a MethodGroup followed by another
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # group.
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while isinstance(expected, MethodGroup):
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expected, method = expected.MethodCalled(self)
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if method is not None:
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return method
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # This is a mock method, so just check equality.
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if expected != self:
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise UnexpectedMethodCallError(self, expected)
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return expected
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __str__(self):
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    params = ', '.join(
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        [repr(p) for p in self._params or []] +
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ['%s=%r' % x for x in sorted((self._named_params or {}).items())])
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    desc = "%s(%s) -> %r" % (self._name, params, self._return_value)
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return desc
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __eq__(self, rhs):
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Test whether this MockMethod is equivalent to another MockMethod.
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # rhs: the right hand side of the test
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rhs: MockMethod
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return (isinstance(rhs, MockMethod) and
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._name == rhs._name and
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._params == rhs._params and
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._named_params == rhs._named_params)
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __ne__(self, rhs):
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Test whether this MockMethod is not equivalent to another MockMethod.
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # rhs: the right hand side of the test
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rhs: MockMethod
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return not self == rhs
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetPossibleGroup(self):
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Returns a possible group from the end of the call queue or None if no
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    other methods are on the stack.
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Remove this method from the tail of the queue so we can add it to a group.
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this_method = self._call_queue.pop()
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert this_method == self
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Determine if the tail of the queue is a group, or just a regular ordered
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # mock method.
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    group = None
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      group = self._call_queue[-1]
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except IndexError:
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pass
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return group
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _CheckAndCreateNewGroup(self, group_name, group_class):
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Checks if the last method (a possible group) is an instance of our
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    group_class. Adds the current method to this group or creates a new one.
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      group_name: the name of the group.
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      group_class: the class used to create instance of this new group
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    group = self.GetPossibleGroup()
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If this is a group, and it is the correct group, add the method.
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if isinstance(group, group_class) and group.group_name() == group_name:
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      group.AddMethod(self)
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return self
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Create a new group and add the method.
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_group = group_class(group_name)
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_group.AddMethod(self)
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._call_queue.append(new_group)
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def InAnyOrder(self, group_name="default"):
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Move this method into a group of unordered calls.
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    A group of unordered calls must be defined together, and must be executed
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    in full before the next expected method can be called.  There can be
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    multiple groups that are expected serially, if they are given
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    different group names.  The same group name can be reused if there is a
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    standard method call, or a group with a different name, spliced between
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    usages.
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      group_name: the name of the unordered group.
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._CheckAndCreateNewGroup(group_name, UnorderedGroup)
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def MultipleTimes(self, group_name="default"):
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Move this method into group of calls which may be called multiple times.
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    A group of repeating calls must be defined together, and must be executed in
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    full before the next expected mehtod can be called.
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      group_name: the name of the unordered group.
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._CheckAndCreateNewGroup(group_name, MultipleTimesGroup)
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AndReturn(self, return_value):
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Set the value to return when this method is called.
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # return_value can be anything.
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._return_value = return_value
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return return_value
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AndRaise(self, exception):
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Set the exception to raise when this method is called.
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # exception: the exception to raise when this method is called.
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exception: Exception
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._exception = exception
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def WithSideEffects(self, side_effects):
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Set the side effects that are simulated when this method is called.
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      side_effects: A callable which modifies the parameters or other relevant
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        state which a given test case depends on.
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Self for chaining with AndReturn and AndRaise.
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._side_effects = side_effects
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Comparator:
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Base class for all Mox comparators.
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  A Comparator can be used as a parameter to a mocked method when the exact
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  value is not known.  For example, the code you are testing might build up a
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  long SQL string that is passed to your mock DAO. You're only interested that
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  the IN clause contains the proper primary keys, so you can set your mock
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  up as follows:
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mock_dao.RunQuery(StrContains('IN (1, 2, 4, 5)')).AndReturn(mock_result)
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Now whatever query is passed in must contain the string 'IN (1, 2, 4, 5)'.
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  A Comparator may replace one or more parameters, for example:
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # return at most 10 rows
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mock_dao.RunQuery(StrContains('SELECT'), 10)
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  or
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Return some non-deterministic number of rows
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mock_dao.RunQuery(StrContains('SELECT'), IsA(int))
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def equals(self, rhs):
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Special equals method that all comparators must implement.
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rhs: any python object
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError, 'method must be implemented by a subclass.'
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __eq__(self, rhs):
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.equals(rhs)
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __ne__(self, rhs):
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return not self.equals(rhs)
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class IsA(Comparator):
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """This class wraps a basic Python type or class.  It is used to verify
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  that a parameter is of the given type or class.
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Example:
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mock_dao.Connect(IsA(DbConnectInfo))
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, class_name):
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initialize IsA
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      class_name: basic python type or a class
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._class_name = class_name
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def equals(self, rhs):
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Check to see if the RHS is an instance of class_name.
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # rhs: the right hand side of the test
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rhs: object
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bool
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return isinstance(rhs, self._class_name)
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except TypeError:
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Check raw types if there was a type error.  This is helpful for
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # things like cStringIO.StringIO.
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return type(rhs) == type(self._class_name)
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __repr__(self):
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return str(self._class_name)
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class IsAlmost(Comparator):
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Comparison class used to check whether a parameter is nearly equal
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  to a given value.  Generally useful for floating point numbers.
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Example mock_dao.SetTimeout((IsAlmost(3.9)))
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, float_value, places=7):
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initialize IsAlmost.
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      float_value: The value for making the comparison.
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      places: The number of decimal places to round to.
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._float_value = float_value
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._places = places
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def equals(self, rhs):
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Check to see if RHS is almost equal to float_value
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rhs: the value to compare to float_value
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bool
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return round(rhs-self._float_value, self._places) == 0
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except TypeError:
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # This is probably because either float_value or rhs is not a number.
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __repr__(self):
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return str(self._float_value)
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class StrContains(Comparator):
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Comparison class used to check whether a substring exists in a
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string parameter.  This can be useful in mocking a database with SQL
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  passed in as a string parameter, for example.
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Example:
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mock_dao.RunQuery(StrContains('IN (1, 2, 4, 5)')).AndReturn(mock_result)
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, search_string):
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initialize.
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # search_string: the string you are searching for
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      search_string: str
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._search_string = search_string
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def equals(self, rhs):
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Check to see if the search_string is contained in the rhs string.
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # rhs: the right hand side of the test
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rhs: object
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bool
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return rhs.find(self._search_string) > -1
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception:
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __repr__(self):
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '<str containing \'%s\'>' % self._search_string
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Regex(Comparator):
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Checks if a string matches a regular expression.
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This uses a given regular expression to determine equality.
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, pattern, flags=0):
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initialize.
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # pattern is the regular expression to search for
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pattern: str
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # flags passed to re.compile function as the second argument
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      flags: int
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.regex = re.compile(pattern, flags=flags)
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def equals(self, rhs):
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Check to see if rhs matches regular expression pattern.
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bool
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.regex.search(rhs) is not None
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __repr__(self):
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s = '<regular expression \'%s\'' % self.regex.pattern
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.regex.flags:
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      s += ', flags=%d' % self.regex.flags
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s += '>'
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return s
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class In(Comparator):
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Checks whether an item (or key) is in a list (or dict) parameter.
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Example:
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mock_dao.GetUsersInfo(In('expectedUserName')).AndReturn(mock_result)
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, key):
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initialize.
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # key is any thing that could be in a list or a key in a dict
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._key = key
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def equals(self, rhs):
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Check to see whether key is in rhs.
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rhs: dict
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bool
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._key in rhs
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __repr__(self):
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '<sequence or map containing \'%s\'>' % self._key
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ContainsKeyValue(Comparator):
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Checks whether a key/value pair is in a dict parameter.
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Example:
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mock_dao.UpdateUsers(ContainsKeyValue('stevepm', stevepm_user_info))
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, key, value):
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initialize.
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # key: a key in a dict
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # value: the corresponding value
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._key = key
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._value = value
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def equals(self, rhs):
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Check whether the given key/value pair is in the rhs dict.
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bool
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return rhs[self._key] == self._value
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception:
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __repr__(self):
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '<map containing the entry \'%s: %s\'>' % (self._key, self._value)
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class SameElementsAs(Comparator):
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Checks whether iterables contain the same elements (ignoring order).
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Example:
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mock_dao.ProcessUsers(SameElementsAs('stevepm', 'salomaki'))
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, expected_seq):
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initialize.
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expected_seq: a sequence
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._expected_seq = expected_seq
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def equals(self, actual_seq):
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Check to see whether actual_seq has same elements as expected_seq.
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      actual_seq: sequence
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bool
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expected = dict([(element, None) for element in self._expected_seq])
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      actual = dict([(element, None) for element in actual_seq])
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except TypeError:
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Fall back to slower list-compare if any of the objects are unhashable.
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expected = list(self._expected_seq)
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      actual = list(actual_seq)
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expected.sort()
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      actual.sort()
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return expected == actual
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __repr__(self):
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '<sequence with same elements as \'%s\'>' % self._expected_seq
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class And(Comparator):
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Evaluates one or more Comparators on RHS and returns an AND of the results.
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, *args):
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initialize.
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *args: One or more Comparator
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._comparators = args
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def equals(self, rhs):
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Checks whether all Comparators are equal to rhs.
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # rhs: can be anything
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bool
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for comparator in self._comparators:
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not comparator.equals(rhs):
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return False
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __repr__(self):
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '<AND %s>' % str(self._comparators)
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Or(Comparator):
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Evaluates one or more Comparators on RHS and returns an OR of the results.
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, *args):
10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initialize.
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *args: One or more Mox comparators
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._comparators = args
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def equals(self, rhs):
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Checks whether any Comparator is equal to rhs.
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # rhs: can be anything
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bool
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for comparator in self._comparators:
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if comparator.equals(rhs):
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return True
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return False
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __repr__(self):
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '<OR %s>' % str(self._comparators)
11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Func(Comparator):
11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Call a function that should verify the parameter passed in is correct.
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  You may need the ability to perform more advanced operations on the parameter
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  in order to validate it.  You can use this to have a callable validate any
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parameter. The callable should return either True or False.
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Example:
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def myParamValidator(param):
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Advanced logic here
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mock_dao.DoSomething(Func(myParamValidator), true)
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, func):
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initialize.
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      func: callable that takes one parameter and returns a bool
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._func = func
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def equals(self, rhs):
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Test whether rhs passes the function test.
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rhs is passed into func.
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rhs: any python object
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      the result of func(rhs)
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._func(rhs)
11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __repr__(self):
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return str(self._func)
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class IgnoreArg(Comparator):
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Ignore an argument.
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This can be used when we don't care about an argument of a method call.
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Example:
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Check if CastMagic is called with 3 as first arg and 'disappear' as third.
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mymock.CastMagic(3, IgnoreArg(), 'disappear')
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def equals(self, unused_rhs):
11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Ignores arguments and returns True.
11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unused_rhs: any python object
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      always returns True
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __repr__(self):
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '<IgnoreArg>'
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MethodGroup(object):
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Base class containing common behaviour for MethodGroups."""
11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, group_name):
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._group_name = group_name
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def group_name(self):
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._group_name
11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __str__(self):
11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '<%s "%s">' % (self.__class__.__name__, self._group_name)
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AddMethod(self, mock_method):
11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError
11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def MethodCalled(self, mock_method):
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def IsSatisfied(self):
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class UnorderedGroup(MethodGroup):
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """UnorderedGroup holds a set of method calls that may occur in any order.
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This construct is helpful for non-deterministic events, such as iterating
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  over the keys of a dict.
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, group_name):
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    super(UnorderedGroup, self).__init__(group_name)
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._methods = []
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AddMethod(self, mock_method):
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Add a method to this group.
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mock_method: A mock method to be added to this group.
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._methods.append(mock_method)
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def MethodCalled(self, mock_method):
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Remove a method call from the group.
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    If the method is not in the set, an UnexpectedMethodCallError will be
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raised.
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mock_method: a mock method that should be equal to a method in the group.
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The mock method from the group
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UnexpectedMethodCallError if the mock_method was not in the group.
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Check to see if this method exists, and if so, remove it from the set
12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # and return it.
12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for method in self._methods:
12425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if method == mock_method:
12435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Remove the called mock_method instead of the method in the group.
12445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # The called method will match any comparators when equality is checked
12455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # during removal.  The method in the group could pass a comparator to
12465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # another comparator during the equality check.
12475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._methods.remove(mock_method)
12485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # If this group is not empty, put it back at the head of the queue.
12505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if not self.IsSatisfied():
12515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          mock_method._call_queue.appendleft(self)
12525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return self, method
12545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise UnexpectedMethodCallError(mock_method, self)
12565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def IsSatisfied(self):
12585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Return True if there are not any methods in this group."""
12595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return len(self._methods) == 0
12615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MultipleTimesGroup(MethodGroup):
12645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """MultipleTimesGroup holds methods that may be called any number of times.
12655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Note: Each method must be called at least once.
12675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This is helpful, if you don't know or care how many times a method is called.
12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, group_name):
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    super(MultipleTimesGroup, self).__init__(group_name)
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._methods = set()
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._methods_called = set()
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AddMethod(self, mock_method):
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Add a method to this group.
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mock_method: A mock method to be added to this group.
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._methods.add(mock_method)
12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def MethodCalled(self, mock_method):
12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Remove a method call from the group.
12875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    If the method is not in the set, an UnexpectedMethodCallError will be
12895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raised.
12905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
12925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mock_method: a mock method that should be equal to a method in the group.
12935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
12955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The mock method from the group
12965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
12985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UnexpectedMethodCallError if the mock_method was not in the group.
12995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
13005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Check to see if this method exists, and if so add it to the set of
13025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # called methods.
13035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for method in self._methods:
13055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if method == mock_method:
13065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._methods_called.add(mock_method)
13075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Always put this group back on top of the queue, because we don't know
13085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # when we are done.
13095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mock_method._call_queue.appendleft(self)
13105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return self, method
13115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.IsSatisfied():
13135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_method = mock_method._PopNextMethod();
13145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return next_method, None
13155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
13165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise UnexpectedMethodCallError(mock_method, self)
13175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def IsSatisfied(self):
13195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Return True if all methods in this group are called at least once."""
13205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # NOTE(psycho): We can't use the simple set difference here because we want
13215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # to match different parameters which are considered the same e.g. IsA(str)
13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # and some string. This solution is O(n^2) but n should be small.
13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tmp = self._methods.copy()
13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for called in self._methods_called:
13255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for expected in tmp:
13265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if called == expected:
13275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          tmp.remove(expected)
13285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if not tmp:
13295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            return True
13305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break
13315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return False
13325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MoxMetaTestBase(type):
13355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Metaclass to add mox cleanup and verification to every test.
13365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  As the mox unit testing class is being constructed (MoxTestBase or a
13385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subclass), this metaclass will modify all test functions to call the
13395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CleanUpMox method of the test class after they finish. This means that
13405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unstubbing and verifying will happen for every test with no additional code,
13415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  and any failures will result in test failures as opposed to errors.
13425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
13435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(cls, name, bases, d):
13455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    type.__init__(cls, name, bases, d)
13465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # also get all the attributes from the base classes to account
13485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # for a case when test class is not the immediate child of MoxTestBase
13495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for base in bases:
13505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for attr_name in dir(base):
13515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        d[attr_name] = getattr(base, attr_name)
13525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for func_name, func in d.items():
13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if func_name.startswith('test') and callable(func):
13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        setattr(cls, func_name, MoxMetaTestBase.CleanUpTest(cls, func))
13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  @staticmethod
13585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CleanUpTest(cls, func):
13595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Adds Mox cleanup code to any MoxTestBase method.
13605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Always unsets stubs after a test. Will verify all mocks for tests that
13625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    otherwise pass.
13635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
13655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cls: MoxTestBase or subclass; the class whose test method we are altering.
13665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      func: method; the method of the MoxTestBase test class we wish to alter.
13675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
13695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The modified method.
13705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
13715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    def new_method(self, *args, **kwargs):
13725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mox_obj = getattr(self, 'mox', None)
13735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cleanup_mox = False
13745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if mox_obj and isinstance(mox_obj, Mox):
13755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        cleanup_mox = True
13765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try:
13775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        func(self, *args, **kwargs)
13785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      finally:
13795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if cleanup_mox:
13805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          mox_obj.UnsetStubs()
13815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if cleanup_mox:
13825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mox_obj.VerifyAll()
13835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_method.__name__ = func.__name__
13845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_method.__doc__ = func.__doc__
13855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_method.__module__ = func.__module__
13865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return new_method
13875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MoxTestBase(unittest.TestCase):
13905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Convenience test class to make stubbing easier.
13915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Sets up a "mox" attribute which is an instance of Mox - any mox tests will
13935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  want this. Also automatically unsets any stubs and verifies that all mock
13945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  methods have been called at the end of each test, eliminating boilerplate
13955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  code.
13965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
13975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  __metaclass__ = MoxMetaTestBase
13995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def setUp(self):
14015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.mox = Mox()
1402