#!/usr/bin/python
import unittest
import tempfile
import inspect
import shutil
import sys
import os
import gc

from types import ModuleType

try:
    import coverage
except ImportError:
    coverage = None
else:
    # Start coverage check before importing from mocker, to get all of it.
    coverage.erase()
    coverage.start()

from mocker import \
    MockerBase, Mocker, Mock, Event, Task, Action, Path, recorder, expect, \
    PathMatcher, path_matcher_recorder, RunCounter, ImplicitRunCounter, \
    run_counter_recorder, run_counter_removal_recorder, MockReturner, \
    mock_returner_recorder, FunctionRunner, Orderer, SpecChecker, \
    spec_checker_recorder, match_params, ANY, IS, CONTAINS, IN, MATCH, ARGS, \
    KWARGS, MatchError, PathExecuter, ProxyReplacer, Patcher, Undefined, \
    PatchedMethod, MockerTestCase, ReplayRestoreEvent, OnRestoreCaller, Expect


class TestCase(unittest.TestCase):
    """Python 2.3 lacked a couple of useful aliases."""
    
    assertTrue = unittest.TestCase.failUnless
    assertFalse = unittest.TestCase.failIf


class CleanMocker(MockerBase):
    """Just a better name for MockerBase in a testing context."""


class IntegrationTest(TestCase):

    def setUp(self):
        self.mocker = Mocker()

    def tearDown(self):
        self.mocker.restore()

    def test_count(self):
        obj = self.mocker.mock()
        obj.x
        self.mocker.count(2, 3)

        self.mocker.replay()
        obj.x
        self.assertRaises(AssertionError, self.mocker.verify)
        obj.x
        self.mocker.verify()
        obj.x
        self.mocker.verify()
        self.assertRaises(AssertionError, getattr, obj, "x")

    def test_order(self):
        obj = self.mocker.mock()

        with_manager = self.mocker.order()
        with_manager.__enter__()
        obj.x
        obj.y
        obj.z
        with_manager.__exit__(None, None, None)

        self.mocker.replay()
        self.assertRaises(AssertionError, getattr, obj, "y")
        self.assertRaises(AssertionError, getattr, obj, "z")

        self.mocker.replay()
        obj.x
        self.assertRaises(AssertionError, getattr, obj, "z")

        self.mocker.replay()
        obj.x
        obj.y
        obj.z

    def test_spec_and_type(self):
        class C(object):
            def m(self, a): pass
        
        obj = self.mocker.mock(C)

        obj.m(1)
        obj.m(a=1)
        obj.m(1, 2)
        obj.m(b=2)
        obj.x()
        obj.y()
        self.mocker.nospec()
        obj.z()

        self.mocker.replay()

        self.assertTrue(isinstance(obj, C))

        obj.m(1)
        obj.m(a=1)
        obj.y()
        self.assertRaises(AssertionError, obj.m, 1, 2)
        self.assertRaises(AssertionError, obj.m, b=2)
        self.assertRaises(AssertionError, obj.x)
        self.assertRaises(AssertionError, obj.z)

    def test_result(self):
        obj = self.mocker.mock()
        obj.x
        self.mocker.result(42)
        self.mocker.replay()
        self.assertEquals(obj.x, 42)

    def test_throw(self):
        obj = self.mocker.mock()
        obj.x()
        self.mocker.throw(ValueError)
        self.mocker.replay()
        self.assertRaises(ValueError, obj.x)

    def test_call(self):
        calls = []
        def func(arg):
            calls.append(arg)
            return 42
        obj = self.mocker.mock()
        obj.x(24)
        self.mocker.call(func)
        self.mocker.replay()
        self.assertEquals(obj.x(24), 42)
        self.assertEquals(calls, [24])

    def test_call_result(self):
        calls = []
        def func(arg):
            calls.append(arg)
            return arg
        obj = self.mocker.mock()
        obj.x(24)
        self.mocker.call(func)
        self.mocker.result(42)
        self.mocker.replay()
        self.assertEquals(obj.x(24), 42)
        self.assertEquals(calls, [24])

    def test_generate(self):
        obj = self.mocker.mock()
        obj.x(24)
        self.mocker.generate([1, 2, 3])
        self.mocker.replay()
        result = obj.x(24)
        def g(): yield None
        self.assertEquals(type(result), type(g()))
        self.assertEquals(list(result), [1, 2, 3])

    def test_proxy(self):
        class C(object):
            def sum(self, *args):
                return sum(args)
        
        obj = self.mocker.proxy(C())
        expect(obj.multiply(2, 3)).result(6).nospec()
        expect(obj.sum(0, 0)).result(1)
        expect(obj.sum(0, 0)).passthrough()

        self.mocker.replay()

        self.assertEquals(obj.multiply(2, 3), 6) # Mocked.
        self.assertRaises(AttributeError, obj.multiply) # Passed through.

        self.assertEquals(obj.sum(2, 3), 5) # Passed through.
        self.assertEquals(obj.sum(0, 0), 1) # Mocked.
        self.assertEquals(obj.sum(0, 0), 0) # Passed through explicitly.
        self.assertRaises(AssertionError, obj.sum, 0, 0) # Seen twice.

    def test_replace_install_and_restore(self):
        module = self.mocker.replace("calendar")
        import calendar
        self.assertTrue(calendar is not module)
        self.mocker.replay()
        import calendar
        self.assertTrue(calendar is module)
        self.mocker.restore()
        import calendar
        self.assertTrue(calendar is not module)

    def test_replace_os_path_join(self):
        path = self.mocker.replace("os.path")
        expect(path.join(ARGS)).call(lambda *args: "-".join(args))
        expect(path.join("e", ARGS)).passthrough()
        self.mocker.replay()
        import os
        self.assertEquals(os.path.join("a", "b", "c"), "a-b-c")
        self.assertNotEquals(os.path.join("e", "f", "g"), "e-f-g")

    def test_replace_os_path_isfile(self):
        path = self.mocker.replace("os.path")
        expect(path.isfile("unexistent")).result(True)
        expect(path.isfile(ANY)).passthrough().count(2)
        self.mocker.replay()
        import os
        self.assertFalse(os.path.isfile("another-unexistent"))
        self.assertTrue(os.path.isfile("unexistent"))
        self.assertFalse(os.path.isfile("unexistent"))

    def test_replace_class_method(self):
        empty = self.mocker.replace("Queue.Queue.empty")
        expect(empty()).result(False)
        self.mocker.replay()
        from Queue import Queue
        self.assertEquals(Queue().empty(), False)
        
    def test_patch_with_spec(self):
        class C(object):
            def method(self, a, b):
                pass
        mock = self.mocker.patch(C)
        mock.method(1, 2)
        mock.method(1)
        self.mocker.replay()
        mock.method(1, 2)
        self.assertRaises(AssertionError, mock.method, 1)

    def test_patch_with_spec_and_unexistent(self):
        class C(object):
            pass
        mock = self.mocker.patch(C)
        mock.method(1, 2)
        self.mocker.count(0)
        self.mocker.replay()
        self.assertRaises(AssertionError, self.mocker.verify)

    def test_mock_iter(self):
        """
        list() uses len() as a hint. When we mock iter(), it shouldn't
        explode due to the lack of len().
        """
        mock = self.mocker.mock()
        iter(mock)
        self.mocker.result(iter([1, 2, 3]))
        self.mocker.replay()
        self.assertEquals(list(mock), [1, 2, 3])
        self.mocker.verify()

    def test_replace_builtin_function(self):
        """
        Inspection doesn't work on builtin functions, but proxying should
        work even then (without spec enforcement in these cases).
        """
        from zlib import adler32
        mock = self.mocker.proxy(adler32)
        mock()
        self.mocker.result(42)
        self.mocker.replay()
        self.assertEquals(mock(), 42)


class ExpectTest(TestCase):

    def setUp(self):
        self.mocker = CleanMocker()

    def test_calling_mocker(self):
        obj = self.mocker.mock()
        expect(obj.attr).result(123)
        self.mocker.replay()
        self.assertEquals(obj.attr, 123)

    def test_chaining(self):
        obj = self.mocker.mock()
        expect(obj.attr).result(123).result(42)
        self.mocker.replay()
        self.assertEquals(obj.attr, 42)

    def test_explicit_expect_instance(self):
        obj = self.mocker.mock()
        myexpect = Expect(self.mocker)
        myexpect(iter(obj)).generate([1, 2, 3]).count(1, 2)
        self.mocker.replay()
        self.assertEquals(list(obj), [1, 2, 3])


class MockerTestCaseTest(TestCase):

    def setUp(self):
        self.test = MockerTestCase("shortDescription")

    def tearDown(self):
        self.test.mocker.restore()
        # Run it so that any cleanups are performed.
        self.test.run()

    def test_has_mocker(self):
        self.assertEquals(type(self.test.mocker), Mocker)

    def test_has_expect(self):
        self.assertTrue(issubclass(self.test.expect, expect))

    def test_expect_works_with_non_mocks(self):
        # We must be using the Expect helper for this to work at all:
        obj = self.test.mocker.mock()
        self.test.expect(iter(obj)).generate([1,2,3])

    def test_attributes_are_the_same(self):
        class MyTest(MockerTestCase):
            def test_method(self):
                pass
            test_method.foo = "bar"
        test = MyTest("test_method")
        self.assertEquals(getattr(test.test_method, "im_class", None), MyTest)
        self.assertEquals(getattr(test.test_method, "foo", None), "bar")

    def test_constructor_is_the_same(self):
        self.assertEquals(inspect.getargspec(TestCase.__init__),
                          inspect.getargspec(MockerTestCase.__init__))

    def test_docstring_is_the_same(self):
        class MyTest(MockerTestCase):
            def test_method(self):
                """Hello there!"""
        self.assertEquals(MyTest("test_method").test_method.__doc__,
                          "Hello there!")

    def test_short_description_is_the_same(self):
        class MyTest(MockerTestCase):
            def test_method(self):
                """Hello there!"""
        class StandardTest(TestCase):
            def test_method(self):
                """Hello there!"""

        self.assertEquals(MyTest("test_method").shortDescription(),
                          StandardTest("test_method").shortDescription())

    def test_missing_method_raises_the_same_error(self):
        class MyTest(TestCase):
            pass

        try:
            MyTest("unexistent_method").run()
        except Exception, e:
            expected_error = e

        class MyTest(MockerTestCase):
            pass
        
        try:
            MyTest("unexistent_method").run()
        except Exception, e:
            self.assertEquals(str(e), str(expected_error))
            self.assertEquals(type(e), type(expected_error))

    def test_raises_runtime_error_if_not_in_replay_mode_with_events(self):
        class MyTest(MockerTestCase):
            def test_method(self):
                pass

        test = MyTest("test_method")

        # That's fine.
        test.test_method()

        test.mocker.add_event(Event())

        # That's not.
        self.assertRaises(RuntimeError, test.test_method)

        test.mocker.replay()

        # Fine again.
        test.test_method()

    def test_mocker_is_verified_and_restored_after_test_method_is_run(self):
        calls = []
        class MyEvent(Event):
            def verify(self):
                calls.append("verify")
            def restore(self):
                calls.append("restore")
        class MyTest(MockerTestCase):
            def test_method(self):
                self.mocker.add_event(MyEvent())
                self.mocker.replay()
            def test_method_raising(self):
                self.mocker.add_event(MyEvent())
                self.mocker.replay()
                raise AssertionError("BOOM!")

        result = unittest.TestResult()
        MyTest("test_method").run(result)

        self.assertEquals(calls, ["verify", "restore"])
        self.assertTrue(result.wasSuccessful())

        del calls[:]

        result = unittest.TestResult()
        MyTest("test_method_raising").run(result)

        self.assertEquals(calls, ["restore"])
        self.assertEquals(len(result.errors), 0)
        self.assertEquals(len(result.failures), 1)
        self.assertTrue("BOOM!" in result.failures[0][1])

        del calls[:]

        result = unittest.TestResult()
        # Running twice in the same instance (Trial does that).
        test = MyTest("test_method")
        test.run(result)
        test.run(result)

        self.assertEquals(calls, ["verify", "restore", "verify", "restore"])
        self.assertTrue(result.wasSuccessful())

    def test_expectation_failure_acts_appropriately(self):
        class MyTest(MockerTestCase):
            def test_method(self):
                mock = self.mocker.mock()
                mock.x
                self.mocker.replay()
        
        result = unittest.TestResult()
        MyTest("test_method").run(result)

        self.assertEquals(len(result.errors), 0)
        self.assertEquals(len(result.failures), 1)
        self.assertTrue("mock.x" in result.failures[0][1])

    def test_add_cleanup(self):
        stash = []
        def func(a, b):
            stash.append((a, b))

        class MyTest(MockerTestCase):
            def tearDown(self):
                self.addCleanup(func, 3, b=4)
            def test_method(self):
                self.addCleanup(func, 1, b=2)
                stash.append(stash[:])

        MyTest("test_method").run()

        self.assertEquals(stash, [[], (1, 2), (3, 4)])

    def test_cleanup_wrapper_in__call__for_2_3(self):
        version_info = sys.version_info
        __call__ = unittest.TestCase.__call__
        try:
            sys.version_info = (2, 3, 5)
            stash = []
            def call(self, *args, **kwargs):
                self.addCleanup(lambda: stash.append(True))
            unittest.TestCase.__call__  = call
            class MyTest(MockerTestCase):
                def test_method(self):
                    pass
            MyTest("test_method")()
            self.assertEquals(stash, [True])
        finally:
            unittest.TestCase.__call__ = __call__
            sys.version_info = version_info

    def test_cleanup_wrapper_in__call__for_2_4(self):
        version_info = sys.version_info
        __call__ = unittest.TestCase.__call__
        try:
            sys.version_info = (2, 4)
            stash = []
            def call(self, *args, **kwargs):
                self.addCleanup(lambda: stash.append(True))
            unittest.TestCase.__call__  = call
            class MyTest(MockerTestCase):
                def test_method(self):
                    pass
            MyTest("test_method")()
            # Python 2.4+ handles cleanup in run(), registered inside
            # MockerTestCase.__init__, so this should *not* work.
            self.assertEquals(stash, [])
        finally:
            unittest.TestCase.__call__ = __call__
            sys.version_info = version_info

    def test_twisted_trial_deferred_support(self):
        calls = []
        callbacks = []
        errbacks = []
        deferreds = []
        class Deferred(object):
            def addCallback(self, callback):
                callbacks.append(callback)
            def addErrback(self, errback):
                errbacks.append(errback)
        class MyEvent(Event):
            def verify(self):
                calls.append("verify")
            def restore(self):
                calls.append("restore")
        class MyTest(MockerTestCase):
            def test_method(self):
                self.mocker.add_event(MyEvent())
                self.mocker.replay()
                deferred = Deferred()
                deferreds.append(deferred)
                return deferred

        result = unittest.TestResult()
        test = MyTest("test_method")
        deferred = test.test_method()

        self.assertEquals(deferred, deferreds[-1])
        self.assertEquals(calls, [])
        self.assertEquals(len(callbacks), 1)
        self.assertEquals(callbacks[-1]("foo"), "foo")
        self.assertEquals(calls, ["verify"])


    def test_fail_unless_is_raises_on_mismatch(self):
        try:
            self.test.failUnlessIs([], [])
        except AssertionError, e:
            self.assertEquals(str(e), "[] is not []")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_is_uses_msg(self):
        try:
            self.test.failUnlessIs([], [], "oops!")
        except AssertionError, e:
            self.assertEquals(str(e), "oops!")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_is_succeeds(self):
        obj = []
        try:
            self.test.failUnlessIs(obj, obj)
        except AssertionError:
            self.fail("AssertionError shouldn't be raised")

    def test_fail_if_is_raises_on_mismatch(self):
        obj = []
        try:
            self.test.failIfIs(obj, obj)
        except AssertionError, e:
            self.assertEquals(str(e), "[] is []")
        else:
            self.fail("AssertionError not raised")

    def test_fail_if_is_uses_msg(self):
        obj = []
        try:
            self.test.failIfIs(obj, obj, "oops!")
        except AssertionError, e:
            self.assertEquals(str(e), "oops!")
        else:
            self.fail("AssertionError not raised")

    def test_fail_if_is_succeeds(self):
        try:
            self.test.failIfIs([], [])
        except AssertionError:
            self.fail("AssertionError shouldn't be raised")

    def test_fail_unless_in_raises_on_mismatch(self):
        try:
            self.test.failUnlessIn(1, [])
        except AssertionError, e:
            self.assertEquals(str(e), "1 not in []")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_in_uses_msg(self):
        try:
            self.test.failUnlessIn(1, [], "oops!")
        except AssertionError, e:
            self.assertEquals(str(e), "oops!")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_in_succeeds(self):
        try:
            self.test.failUnlessIn(1, [1])
        except AssertionError:
            self.fail("AssertionError shouldn't be raised")

    def test_fail_if_in_raises_on_mismatch(self):
        try:
            self.test.failIfIn(1, [1])
        except AssertionError, e:
            self.assertEquals(str(e), "1 in [1]")
        else:
            self.fail("AssertionError not raised")

    def test_fail_if_in_uses_msg(self):
        try:
            self.test.failIfIn(1, [1], "oops!")
        except AssertionError, e:
            self.assertEquals(str(e), "oops!")
        else:
            self.fail("AssertionError not raised")

    def test_fail_if_in_succeeds(self):
        try:
            self.test.failIfIn(1, [])
        except AssertionError:
            self.fail("AssertionError shouldn't be raised")

    def test_fail_unless_starts_with_raises_on_mismatch(self):
        try:
            self.test.failUnlessStartsWith("abc", "def")
        except AssertionError, e:
            self.assertEquals(str(e), "'abc' doesn't start with 'def'")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_starts_with_uses_msg(self):
        try:
            self.test.failUnlessStartsWith("abc", "def", "oops!")
        except AssertionError, e:
            self.assertEquals(str(e), "oops!")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_starts_with_succeeds(self):
        try:
            self.test.failUnlessStartsWith("abcdef", "abc")
        except AssertionError:
            self.fail("AssertionError shouldn't be raised")

    def test_fail_unless_starts_with_works_with_non_strings(self):
        self.test.failUnlessStartsWith([1, 2, 3], [1, 2])
        self.assertRaises(AssertionError,
                          self.test.failUnlessStartsWith, [1, 2, 3], [4, 5, 6])

    def test_fail_if_starts_with_raises_on_mismatch(self):
        try:
            self.test.failIfStartsWith("abcdef", "abc")
        except AssertionError, e:
            self.assertEquals(str(e), "'abcdef' starts with 'abc'")
        else:
            self.fail("AssertionError not raised")

    def test_fail_if_starts_with_uses_msg(self):
        try:
            self.test.failIfStartsWith("abcdef", "abc", "oops!")
        except AssertionError, e:
            self.assertEquals(str(e), "oops!")
        else:
            self.fail("AssertionError not raised")

    def test_fail_if_starts_with_succeeds(self):
        try:
            self.test.failIfStartsWith("abc", "def")
        except AssertionError:
            self.fail("AssertionError shouldn't be raised")

    def test_fail_if_starts_with_works_with_non_strings(self):
        self.test.failIfStartsWith([1, 2, 3], [4, 5, 6])
        self.assertRaises(AssertionError,
                          self.test.failIfStartsWith, [1, 2, 3], [1, 2])

    def test_fail_unless_ends_with_raises_on_mismatch(self):
        try:
            self.test.failUnlessEndsWith("abc", "def")
        except AssertionError, e:
            self.assertEquals(str(e), "'abc' doesn't end with 'def'")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_ends_with_uses_msg(self):
        try:
            self.test.failUnlessEndsWith("abc", "def", "oops!")
        except AssertionError, e:
            self.assertEquals(str(e), "oops!")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_ends_with_succeeds(self):
        try:
            self.test.failUnlessEndsWith("abcdef", "def")
        except AssertionError:
            self.fail("AssertionError shouldn't be raised")

    def test_fail_unless_ends_with_works_with_non_strings(self):
        self.test.failUnlessEndsWith([1, 2, 3], [2, 3])
        self.assertRaises(AssertionError,
                          self.test.failUnlessEndsWith, [1, 2, 3], [4, 5, 6])

    def test_fail_if_ends_with_raises_on_mismatch(self):
        try:
            self.test.failIfEndsWith("abcdef", "def")
        except AssertionError, e:
            self.assertEquals(str(e), "'abcdef' ends with 'def'")
        else:
            self.fail("AssertionError not raised")

    def test_fail_if_ends_with_uses_msg(self):
        try:
            self.test.failIfEndsWith("abcdef", "def", "oops!")
        except AssertionError, e:
            self.assertEquals(str(e), "oops!")
        else:
            self.fail("AssertionError not raised")

    def test_fail_if_ends_with_succeeds(self):
        try:
            self.test.failIfEndsWith("abc", "def")
        except AssertionError:
            self.fail("AssertionError shouldn't be raised")

    def test_fail_if_ends_with_works_with_non_strings(self):
        self.test.failIfEndsWith([1, 2, 3], [4, 5, 6])
        self.assertRaises(AssertionError,
                          self.test.failIfEndsWith, [1, 2, 3], [2, 3])

    def test_fail_unless_approximates_raises_on_mismatch(self):
        try:
            self.test.failUnlessApproximates(1, 2, 0.999)
        except AssertionError, e:
            self.assertEquals(str(e), "abs(1 - 2) > 0.999")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_approximates_uses_msg(self):
        try:
            self.test.failUnlessApproximates(1, 2, 0.999, "oops!")
        except AssertionError, e:
            self.assertEquals(str(e), "oops!")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_approximates_succeeds(self):
        try:
            self.test.failUnlessApproximates(1, 2, 1)
        except AssertionError:
            self.fail("AssertionError shouldn't be raised")

    def test_fail_if_approximates_raises_on_mismatch(self):
        try:
            self.test.failIfApproximates(1, 2, 1)
        except AssertionError, e:
            self.assertEquals(str(e), "abs(1 - 2) <= 1")
        else:
            self.fail("AssertionError not raised")

    def test_fail_if_approximates_uses_msg(self):
        try:
            self.test.failIfApproximates(1, 2, 1, "oops!")
        except AssertionError, e:
            self.assertEquals(str(e), "oops!")
        else:
            self.fail("AssertionError not raised")

    def test_fail_if_approximates_succeeds(self):
        try:
            self.test.failIfApproximates(1, 2, 0.999)
        except AssertionError:
            self.fail("AssertionError shouldn't be raised")

    def test_fail_unless_methods_match_raises_on_different_method(self):
        class Fake(object):
            def method(self, a): pass
        class Real(object):
            def method(self, b): pass
        try:
            self.test.failUnlessMethodsMatch(Fake, Real)
        except AssertionError, e:
            self.assertEquals(str(e), "Fake.method(self, a) != "
                                      "Real.method(self, b)")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_methods_match_raises_on_missing_method(self):
        class Fake(object):
            def method(self, a): pass
        class Real(object):
            pass
        try:
            self.test.failUnlessMethodsMatch(Fake, Real)
        except AssertionError, e:
            self.assertEquals(str(e), "Fake.method(self, a) not present "
                                      "in Real")
        else: self.fail("AssertionError not raised")

    def test_fail_unless_methods_match_succeeds_on_missing_priv_method(self):
        class Fake(object):
            def _method(self, a): pass
        class Real(object):
            pass
        try:
            self.test.failUnlessMethodsMatch(Fake, Real)
        except AssertionError, e:
            self.fail("AssertionError shouldn't be raised")

    def test_fail_unless_methods_match_raises_on_different_priv_method(self):
        class Fake(object):
            def _method(self, a): pass
        class Real(object):
            def _method(self, b): pass
        try:
            self.test.failUnlessMethodsMatch(Fake, Real)
        except AssertionError, e:
            self.assertEquals(str(e), "Fake._method(self, a) != "
                                      "Real._method(self, b)")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_methods_match_succeeds(self):
        class Fake(object):
            def method(self, a): pass
        class Real(object):
            def method(self, a): pass
        obj = []
        try:
            self.test.failUnlessMethodsMatch(Fake, Real)
        except AssertionError:
            self.fail("AssertionError shouldn't be raised")

    def test_fail_unless_raises_succeeds(self):
        class MyException(Exception):
            pass
        def f(*args):
            raise MyException(*args)
        error = self.test.failUnlessRaises(MyException, f, 1, "foo")
        self.assertEquals(error.args, (1, "foo"))

    def test_fail_unless_raises_error(self):
        def f(*args):
            return args
        try:
            self.test.failUnlessRaises(ValueError, f, 1, "foo")
        except AssertionError, e:
            self.assertEquals(
                str(e),
                "ValueError not raised ((1, 'foo') returned)")
        else:
            self.fail("AssertionError not raised")

    def test_fail_unless_raises_other_exception(self):
        class MyException1(Exception):
            pass
        class MyException2(Exception):
            pass
        def f(*args):
            raise MyException2(*args)
        try:
            self.test.failUnlessRaises(MyException1, f, 1, "foo")
        except MyException2:
            pass
        else:
            self.fail("MyException2 not raised")

    def test_aliases(self):
        get_method = MockerTestCase.__dict__.get

        self.assertEquals(get_method("assertIs"),
                          get_method("failUnlessIs"))

        self.assertEquals(get_method("assertIsNot"),
                          get_method("failIfIs"))

        self.assertEquals(get_method("assertIn"),
                          get_method("failUnlessIn"))

        self.assertEquals(get_method("assertNotIn"),
                          get_method("failIfIn"))

        self.assertEquals(get_method("assertStartsWith"),
                          get_method("failUnlessStartsWith"))

        self.assertEquals(get_method("assertNotStartsWith"),
                          get_method("failIfStartsWith"))

        self.assertEquals(get_method("assertEndsWith"),
                          get_method("failUnlessEndsWith"))

        self.assertEquals(get_method("assertNotEndsWith"),
                          get_method("failIfEndsWith"))

        self.assertEquals(get_method("assertApproximates"),
                          get_method("failUnlessApproximates"))

        self.assertEquals(get_method("assertNotApproximates"),
                          get_method("failIfApproximates"))

        self.assertEquals(get_method("assertMethodsMatch"),
                          get_method("failUnlessMethodsMatch"))

        self.assertEquals(get_method("assertRaises"),
                          get_method("failUnlessRaises"))

    def test_twisted_trial_aliases(self):
        get_method = MockerTestCase.__dict__.get

        self.assertEquals(get_method("assertIdentical"),
                          get_method("assertIs"))

        self.assertEquals(get_method("assertNotIdentical"),
                          get_method("assertIsNot"))

        self.assertEquals(get_method("failUnlessIdentical"),
                          get_method("failUnlessIs"))

        self.assertEquals(get_method("failIfIdentical"),
                          get_method("failIfIs"))

    def test_missing_python23_aliases(self):
        self.assertEquals(MockerTestCase.assertTrue.im_func,
                          MockerTestCase.failUnless.im_func)

        self.assertEquals(MockerTestCase.assertFalse.im_func,
                          MockerTestCase.failIf.im_func)

    def test_make_file_returns_writable_filename(self):
        filename = self.test.makeFile()
        self.assertFalse(os.path.isfile(filename))
        open(filename, "w").write("Is writable!")

    def test_make_file_creates_file(self):
        filename = self.test.makeFile("")
        self.assertEquals(os.path.getsize(filename), 0)

    def test_make_file_cleansup_on_success(self):
        filename = self.test.makeFile()
        self.test.run()
        self.assertEquals(os.path.isfile(filename), False)

    def test_make_file_cleansup_on_failure(self):
        class MyTest(MockerTestCase):
            def test_method(self):
                raise AssertionError("BOOM!")
        test = MyTest("test_method")
        filename = test.makeFile()
        test.run()
        self.assertEquals(os.path.isfile(filename), False)

    def test_make_file_with_content(self):
        filename = self.test.makeFile("content")
        self.assertEquals(open(filename).read(), "content")

    def test_make_file_with_prefix(self):
        filename = self.test.makeFile(prefix="prefix-")
        self.assertTrue(os.path.basename(filename).startswith("prefix-"))

    def test_make_file_with_suffix(self):
        filename = self.test.makeFile(suffix="-suffix")
        self.assertTrue(os.path.basename(filename).endswith("-suffix"))

    def test_make_file_with_dirname(self):
        dirname = tempfile.mkdtemp()
        try:
            filename = self.test.makeFile(dirname=dirname)
            self.assertEquals(os.path.dirname(filename), dirname)
        finally:
            shutil.rmtree(dirname)

    def test_make_file_with_basename(self):
        filename = self.test.makeFile(basename="basename")
        self.assertEquals(os.path.basename(filename), "basename")
        self.test.run()
        self.assertFalse(os.path.exists(filename))

    def test_make_file_with_basename_and_dirname(self):
        dirname = tempfile.mkdtemp()
        try:
            filename = self.test.makeFile(dirname=dirname, basename="basename")
            self.assertEquals(os.path.dirname(filename), dirname)
            self.assertEquals(os.path.basename(filename), "basename")
        finally:
            shutil.rmtree(dirname)

    def test_make_file_with_path(self):
        path = tempfile.mktemp()
        try:
            filename = self.test.makeFile("", path=path)
            self.assertEquals(filename, path)
            self.assertEquals(os.path.getsize(filename), 0)
            self.test.run()
            self.assertFalse(os.path.exists(filename))
        finally:
            if os.path.isfile(path):
                os.unlink(path)

    def test_make_dir_returns_dirname(self):
        dirname = self.test.makeDir()
        self.assertEquals(os.path.isdir(dirname), True)

    def test_make_dir_cleansup_on_success(self):
        dirname = self.test.makeDir()
        self.test.run()
        self.assertEquals(os.path.isdir(dirname), False)

    def test_make_dir_cleansup_on_failure(self):
        class MyTest(MockerTestCase):
            def test_method(self):
                raise AssertionError("BOOM!")
        test = MyTest("test_method")
        dirname = test.makeDir()
        test.run()
        self.assertEquals(os.path.isdir(dirname), False)

    def test_make_dir_with_prefix(self):
        dirname = self.test.makeDir(prefix="prefix-")
        self.assertTrue(os.path.basename(dirname).startswith("prefix-"))

    def test_make_dir_with_suffix(self):
        dirname = self.test.makeDir(suffix="-suffix")
        self.assertTrue(os.path.basename(dirname).endswith("-suffix"))

    def test_make_dir_with_dirname(self):
        dirname = tempfile.mkdtemp()
        try:
            path = self.test.makeDir(dirname=dirname)
            self.assertEquals(os.path.dirname(path), dirname)
        finally:
            if os.path.exists(dirname):
                shutil.rmtree(dirname)

    def test_make_dir_with_path(self):
        path = tempfile.mktemp()
        try:
            self.assertEquals(self.test.makeDir(path=path), path)
            self.assertEquals(os.path.isdir(path), True)
            self.test.run()
            self.assertEquals(os.path.isdir(path), False)
        finally:
            if os.path.exists(path):
                shutil.rmtree(path)

    def test_mocker_is_restored_between_method_return_and_cleanup(self):
        """
        Certain base classes which MockerTestCase is mixed with may have
        additional logic after the real test method is run, but before
        run() returns.  We don't want to perform mocking checks on these.
        """
        class CustomTestCase(unittest.TestCase):
            def run(self, result):
                import time
                super(CustomTestCase, self).run(result)
                self.assertTrue(time.time())

        class MyTest(CustomTestCase, MockerTestCase):
            def test_method(self):
                import time
                time_mock = self.mocker.replace(time.time)
                time_mock()
                self.mocker.count(0) # Forbit it entirely.
                self.mocker.replay()

        result = unittest.TestResult()
        MyTest("test_method").run(result)

        self.assertEquals(result.errors, [])
        self.assertEquals(result.failures, [])



class MockerTest(TestCase):

    def setUp(self):
        self.recorded = []
        self.mocker = CleanMocker()
        def recorder(mocker, event):
            self.recorded.append((mocker, event))
        self.mocker.add_recorder(recorder)

        self.action = Action("getattr", ("attr",), {},
                             Path(Mock(self.mocker, name="mock")))
        self.path = self.action.path + self.action

    def test_default_is_recording(self):
        self.assertTrue(self.mocker.is_recording())

    def test_replay(self):
        calls = []
        event = self.mocker.add_event(Event())
        task = event.add_task(Task())
        task.replay = lambda: calls.append("replay")
        task.restore = lambda: calls.append("restore")
        self.mocker.replay()
        self.assertFalse(self.mocker.is_recording())
        self.assertEquals(calls, ["replay"])
        self.mocker.replay()
        self.assertEquals(calls, ["replay", "restore", "replay"])

    def test_restore(self):
        calls = []
        event = self.mocker.add_event(Event())
        task = event.add_task(Task())
        task.replay = lambda: calls.append("replay")
        task.restore = lambda: calls.append("restore")
        self.mocker.replay()
        self.mocker.restore()
        self.mocker.restore()
        self.assertTrue(self.mocker.is_recording())
        self.assertEquals(calls, ["replay", "restore"])

    def test_reset(self):
        calls = []
        event = self.mocker.add_event(Event())
        task = event.add_task(Task())
        task.restore = lambda: calls.append("restore")
        self.mocker.replay()
        self.mocker.reset()
        self.mocker.reset()
        self.assertEquals(calls, ["restore"])
        self.assertEquals(self.mocker.get_events(), [])

    def test_reset_removes_ordering(self):
        self.mocker.order()
        self.mocker.reset()
        self.assertFalse(self.mocker.is_ordering())

    def test_verify(self):
        class MyEvent(object):
            def __init__(self, id, failed):
                self.id = id
                self.failed = failed
            def verify(self):
                if self.failed:
                    raise AssertionError("%d failed\n- Line 1\n- Line 2\n"
                                         % self.id)

        self.mocker.add_event(MyEvent(1, True))
        self.mocker.add_event(MyEvent(2, False))
        self.mocker.add_event(MyEvent(3, True))

        try:
            self.mocker.verify()
        except AssertionError, e:
            message = os.linesep.join(["[Mocker] Unmet expectations:",
                                       "",
                                       "=> 1 failed",
                                       " - Line 1",
                                       " - Line 2",
                                       "",
                                       "=> 3 failed",
                                       " - Line 1",
                                       " - Line 2",
                                       ""])
            self.assertEquals(str(e), message)
        else:
            self.fail("AssertionError not raised")

    def test_verify_errors_need_good_messages(self):
        class MyEvent(object):
            def verify(self):
                raise AssertionError()
        self.mocker.add_event(MyEvent())
        self.assertRaises(RuntimeError, self.mocker.verify)

    def test_mocker_as_context_manager(self):
        calls = []
        throw = False
        class MyEvent(Event):
            def verify(self):
                calls.append("verify")
                if throw:
                    raise AssertionError("Some problem")
            def replay(self):
                calls.append("replay")
            def restore(self):
                calls.append("restore")

        event = MyEvent()
        self.mocker.add_event(event)

        self.assertEquals(calls, [])

        mocker = self.mocker.__enter__()
        self.assertTrue(mocker is self.mocker)
        self.assertEquals(calls, ["replay"])

        # Verify without errors.
        del calls[:]
        result = self.mocker.__exit__(None, None, None)
        self.assertEquals(result, False)
        self.assertEquals(calls, ["restore", "verify"])

        throw = True

        # Verify raising an error.
        self.mocker.replay()
        del calls[:]
        self.assertRaises(AssertionError,
                          self.mocker.__exit__, None, None, None)
        self.assertEquals(calls, ["restore", "verify"])

        # An exception happened in the 'with' block.  Verify won't raise.
        self.mocker.replay()
        del calls[:]
        result = self.mocker.__exit__(AssertionError, None, None)
        self.assertEquals(result, False)
        self.assertEquals(calls, ["restore"])

    def test_add_recorder_on_instance(self):
        obj1 = object()
        obj2 = object()
        mocker = CleanMocker()
        self.assertEquals(mocker.add_recorder(obj1), obj1)
        self.assertEquals(mocker.add_recorder(obj2), obj2)
        self.assertEquals(mocker.get_recorders(), [obj1, obj2])
        mocker = CleanMocker()
        self.assertEquals(mocker.add_recorder(obj1), obj1)
        self.assertEquals(mocker.get_recorders(), [obj1])

    def test_add_recorder_on_class(self):
        class MyMocker(CleanMocker):
            pass
        obj1 = object()
        obj2 = object()
        self.assertEquals(MyMocker.add_recorder(obj1), obj1)
        self.assertEquals(MyMocker.add_recorder(obj2), obj2)
        mocker = MyMocker()
        self.assertEquals(mocker.get_recorders(), [obj1, obj2])
        mocker = MyMocker()
        self.assertEquals(mocker.get_recorders(), [obj1, obj2])

    def test_add_recorder_on_subclass(self):
        class MyMocker1(CleanMocker):
            pass
        obj1 = object()
        MyMocker1.add_recorder(obj1)
        class MyMocker2(MyMocker1):
            pass
        obj2 = object()
        MyMocker2.add_recorder(obj2)
        self.assertEquals(MyMocker1.get_recorders(), [obj1])
        self.assertEquals(MyMocker2.get_recorders(), [obj1, obj2])

    def test_remove_recorder_on_instance(self):
        obj1 = object()
        obj2 = object()
        obj3 = object()
        class MyMocker(CleanMocker):
            pass
        MyMocker.add_recorder(obj1)
        MyMocker.add_recorder(obj2)
        MyMocker.add_recorder(obj3)
        mocker = MyMocker()
        mocker.remove_recorder(obj2)
        self.assertEquals(mocker.get_recorders(), [obj1, obj3])
        self.assertEquals(MyMocker.get_recorders(), [obj1, obj2, obj3])

    def test_remove_recorder_on_class(self):
        class MyMocker(CleanMocker):
            pass
        obj1 = object()
        obj2 = object()
        self.assertEquals(MyMocker.add_recorder(obj1), obj1)
        self.assertEquals(MyMocker.add_recorder(obj2), obj2)
        MyMocker.remove_recorder(obj1)
        self.assertEquals(MyMocker.get_recorders(), [obj2])

    def test_mock(self):
        mock = self.mocker.mock()
        self.assertEquals(mock.__mocker_name__, None)
        self.assertEquals(mock.__mocker_spec__, None)
        self.assertEquals(mock.__mocker_type__, None)
        self.assertEquals(mock.__mocker_count__, True)

    def test_mock_with_name(self):
        mock = self.mocker.mock(name="name")
        self.assertEquals(mock.__mocker_name__, "name")

    def test_mock_with_spec(self):
        class C(object): pass
        mock = self.mocker.mock(spec=C)
        self.assertEquals(mock.__mocker_spec__, C)

    def test_mock_with_type(self):
        class C(object): pass
        mock = self.mocker.mock(type=C)
        self.assertEquals(mock.__mocker_type__, C)

    def test_mock_with_spec_and_type(self):
        class C(object): pass
        mock = self.mocker.mock(C)
        self.assertEquals(mock.__mocker_spec__, C)
        self.assertEquals(mock.__mocker_type__, C)

    def test_mock_with_count(self):
        class C(object): pass
        mock = self.mocker.mock(count=False)
        self.assertEquals(mock.__mocker_count__, False)

    def test_proxy(self):
        original = object()
        mock = self.mocker.proxy(original)
        self.assertEquals(type(mock), Mock)
        self.assertEquals(mock.__mocker_object__, original)
        self.assertEquals(mock.__mocker_path__.root_object, original)
        self.assertEquals(mock.__mocker_count__, True)

    def test_proxy_with_count(self):
        original = object()
        mock = self.mocker.proxy(original, count=False)
        self.assertEquals(mock.__mocker_count__, False)

    def test_proxy_with_spec(self):
        original = object()
        class C(object): pass
        mock = self.mocker.proxy(original, C)
        self.assertEquals(mock.__mocker_object__, original)
        self.assertEquals(mock.__mocker_spec__, C)

    def test_proxy_with_type(self):
        original = object()
        class C(object): pass
        mock = self.mocker.proxy(original, type=C)
        self.assertEquals(mock.__mocker_type__, C)

    def test_proxy_spec_defaults_to_the_object_itself(self):
        original = object()
        mock = self.mocker.proxy(original)
        self.assertEquals(mock.__mocker_spec__, original)

    def test_proxy_type_defaults_to_the_object_type(self):
        original = object()
        mock = self.mocker.proxy(original)
        self.assertEquals(mock.__mocker_type__, object)

    def test_proxy_with_spec_and_type_none(self):
        original = object()
        mock = self.mocker.proxy(original, spec=None, type=None)
        self.assertEquals(mock.__mocker_spec__, None)
        self.assertEquals(mock.__mocker_type__, None)

    def test_proxy_with_passthrough_false(self):
        original = object()
        class C(object): pass
        mock = self.mocker.proxy(original, C, passthrough=False)
        self.assertEquals(mock.__mocker_object__, original)
        self.assertEquals(mock.__mocker_spec__, C)
        self.assertEquals(mock.__mocker_passthrough__, False)

    def test_proxy_with_submodule_string(self):
        from os import path
        module = self.mocker.proxy("os.path")
        self.assertEquals(type(module), Mock)
        self.assertEquals(type(module.__mocker_object__), ModuleType)
        self.assertEquals(module.__mocker_name__, "os.path")
        self.assertEquals(module.__mocker_object__, path)

    def test_proxy_with_module_function_string(self):
        mock = self.mocker.proxy("os.path.join.func_name")
        self.assertEquals(mock.__mocker_object__, "join")

    def test_proxy_with_string_and_name(self):
        module = self.mocker.proxy("os.path", name="mock")
        self.assertEquals(module.__mocker_name__, "mock")

    def test_proxy_with_unexistent_module(self):
        self.assertRaises(ImportError, self.mocker.proxy, "unexistent.module")

    def test_replace(self):
        from os import path
        obj = object()
        proxy = self.mocker.replace(obj, spec=object, name="obj", count=False,
                                    passthrough=False)
        self.assertEquals(type(proxy), Mock)
        self.assertEquals(type(proxy.__mocker_object__), object)
        self.assertEquals(proxy.__mocker_object__, obj)
        self.assertEquals(proxy.__mocker_spec__, object)
        self.assertEquals(proxy.__mocker_name__, "obj")
        self.assertEquals(proxy.__mocker_count__, False)
        (event,) = self.mocker.get_events()
        self.assertEquals(type(event), ReplayRestoreEvent)
        (task,) = event.get_tasks()
        self.assertEquals(type(task), ProxyReplacer)
        self.assertTrue(task.mock is proxy)
        self.assertTrue(task.mock.__mocker_object__ is obj)
        self.assertTrue(proxy is not obj)

    def test_replace_with_submodule_string(self):
        from os import path
        module = self.mocker.replace("os.path")
        self.assertEquals(type(module), Mock)
        self.assertEquals(type(module.__mocker_object__), ModuleType)
        self.assertEquals(module.__mocker_name__, "os.path")
        self.assertEquals(module.__mocker_object__, path)
        (event,) = self.mocker.get_events()
        (task,) = event.get_tasks()
        self.assertEquals(type(task), ProxyReplacer)
        self.assertTrue(task.mock is module)
        self.assertTrue(task.mock.__mocker_object__ is path)
        self.assertTrue(module is not path)

    def test_replace_with_module_function_string(self):
        mock = self.mocker.replace("os.path.join.func_name")
        self.assertEquals(mock.__mocker_object__, "join")

    def test_replace_with_string_and_name(self):
        module = self.mocker.replace("os.path", name="mock")
        self.assertEquals(module.__mocker_name__, "mock")

    def test_replace_with_type(self):
        original = object()
        class C(object): pass
        mock = self.mocker.replace(original, type=C)
        self.assertEquals(mock.__mocker_type__, C)

    def test_replace_spec_defaults_to_the_object_itself(self):
        original = object()
        mock = self.mocker.replace(original)
        self.assertEquals(mock.__mocker_spec__, original)

    def test_replace_type_defaults_to_the_object_type(self):
        original = object()
        mock = self.mocker.replace(original)
        self.assertEquals(mock.__mocker_type__, object)

    def test_replace_with_spec_and_type_none(self):
        original = object()
        mock = self.mocker.replace(original, spec=None, type=None)
        self.assertEquals(mock.__mocker_spec__, None)
        self.assertEquals(mock.__mocker_type__, None)

    def test_replace_with_passthrough_false(self):
        original = object()
        class C(object): pass
        mock = self.mocker.replace(original, passthrough=False)
        self.assertEquals(mock.__mocker_passthrough__, False)

    def test_replace_with_bound_method(self):
        from Queue import Queue
        mock = self.mocker.replace(Queue.empty)
        self.assertEquals(mock.__mocker_object__, Queue.empty.im_func)

    def test_add_and_get_event(self):
        self.mocker.add_event(41)
        self.assertEquals(self.mocker.add_event(42), 42)
        self.assertEquals(self.mocker.get_events(), [41, 42])

    def test_recording(self):
        obj = self.mocker.mock()
        obj.attr()

        self.assertEquals(len(self.recorded), 2)

        action1 = Action("getattr", ("attr",), {})
        action2 = Action("call", (), {})

        mocker1, event1 = self.recorded[0]
        self.assertEquals(mocker1, self.mocker)
        self.assertEquals(type(event1), Event)
        self.assertTrue(event1.path.matches(Path(obj, None, [action1])))

        mocker2, event2 = self.recorded[1]
        self.assertEquals(mocker2, self.mocker)
        self.assertEquals(type(event2), Event)
        self.assertTrue(event2.path.matches(Path(obj, None,
                                                 [action1, action2])))

        self.assertEquals(self.mocker.get_events(), [event1, event2])

    def test_recording_result_path(self):
        obj = self.mocker.mock()
        result = obj.attr()
        path = Path(obj, None, [Action("getattr", ("attr",), {}),
                                Action("call", (), {})])
        self.assertTrue(result.__mocker_path__.matches(path))

    def test_replaying_no_events(self):
        self.mocker.replay()
        try:
            self.mocker.act(self.path)
        except AssertionError, e:
            pass
        else:
            self.fail("AssertionError not raised")
        self.assertEquals(str(e), "[Mocker] Unexpected expression: mock.attr")

    def test_replaying_matching(self):
        calls = []
        class MyTask(Task):
            def matches(_, path):
                calls.append("matches")
                self.assertTrue(self.path.matches(path))
                return True
            def run(_, path):
                calls.append("run")
                self.assertTrue(self.path.matches(path))
                return "result"
        event = Event()
        event.add_task(MyTask())
        self.mocker.add_event(event)
        self.mocker.replay()
        self.assertEquals(self.mocker.act(self.path), "result")
        self.assertEquals(calls, ["matches", "run"])

    def test_replaying_none_matching(self):
        calls = []
        class MyTask(Task):
            def matches(_, path):
                self.assertTrue(self.path.matches(path))
                calls.append("matches")
                return False
        event = Event()
        event.add_task(MyTask())
        self.mocker.add_event(event)
        self.mocker.replay()
        self.assertRaises(AssertionError, self.mocker.act, self.path)
        self.assertEquals(calls, ["matches"])

    def test_replay_order(self):
        """
        When playing back, the precedence of events is as follows:

        1. Events with may_run() true
        2. Events with satisfied() false
        3. Events with has_run() false

        """
        class MyTaskBase(Task):
            postpone = 2
            def may_run(self, path):
                if not self.postpone:
                    return True
                self.postpone -= 1
            def run(self, path):
                return self.__class__.__name__
        class MyTask1(MyTaskBase): pass
        class MyTask2(MyTaskBase): pass
        class MyTask3(MyTaskBase):
            raised = False
            def verify(self):
                if not self.postpone and not self.raised:
                    self.raised = True
                    raise AssertionError("An error")
        class MyTask4(MyTaskBase):
            postpone = 0
        class MyTask5(MyTaskBase):
            postpone = 1

        event1 = self.mocker.add_event(Event())
        event1.add_task(MyTask1())
        event2 = self.mocker.add_event(Event())
        event2.add_task(MyTask2())
        event3 = self.mocker.add_event(Event())
        event3.add_task(MyTask3())
        event4 = self.mocker.add_event(Event())
        event4.add_task(MyTask4())
        event5 = self.mocker.add_event(Event())
        event5.add_task(MyTask5())
        self.mocker.replay()

        # Labels: [M]ay run, [S]atisfied, [H]as run

        # State: 1=S 2=S 3= 4=MS 5=S
        self.assertEquals(self.mocker.act(self.path), "MyTask4")
        # State: 1=S 2=S 3= 4=MSH 5=S
        self.assertEquals(self.mocker.act(self.path), "MyTask4")
        # State: 1=MS 2=MS 3=M 4=MSH 5=MS
        self.assertEquals(self.mocker.act(self.path), "MyTask3")
        # State: 1=MS 2=MS 3=MSH 4=MSH 5=MS
        self.assertEquals(self.mocker.act(self.path), "MyTask1")
        # State: 1=MSH 2=MS 3=MSH 4=MSH 5=MS
        self.assertEquals(self.mocker.act(self.path), "MyTask2")
        # State: 1=MSH 2=MSH 3=MSH 4=MSH 5=MS
        self.assertEquals(self.mocker.act(self.path), "MyTask5")
        # State: 1=MSH 2=MSH 3=MSH 4=MSH 5=MSH
        self.assertEquals(self.mocker.act(self.path), "MyTask1")

    def test_recorder_decorator(self):
        result = recorder(42)
        try:
            self.assertEquals(result, 42)
            self.assertEquals(Mocker.get_recorders()[-1], 42)
            self.assertEquals(MockerBase.get_recorders(), [])
        finally:
            Mocker.remove_recorder(42)

    def test_result(self):
        event1 = self.mocker.add_event(Event())
        event2 = self.mocker.add_event(Event())
        self.mocker.result(123)
        self.assertEquals(event2.run(self.path), 123)

    def test_throw(self):
        class MyException(Exception): pass
        event1 = self.mocker.add_event(Event())
        event2 = self.mocker.add_event(Event())
        self.mocker.throw(MyException)
        self.assertRaises(MyException, event2.run, self.path)

    def test_call(self):
        event1 = self.mocker.add_event(Event())
        event2 = self.mocker.add_event(Event())
        self.mocker.call(lambda *args, **kwargs: 123)
        self.assertEquals(event2.run(self.path), 123)

    def test_count(self):
        event1 = self.mocker.add_event(Event())
        event2 = self.mocker.add_event(Event())
        event2.add_task(ImplicitRunCounter(1))
        self.mocker.count(2, 3)
        self.assertEquals(len(event1.get_tasks()), 0)
        (task,) = event2.get_tasks()
        self.assertEquals(type(task), RunCounter)
        self.assertEquals(task.min, 2)
        self.assertEquals(task.max, 3)
        self.mocker.count(4)
        self.assertEquals(len(event1.get_tasks()), 0)
        (task,) = event2.get_tasks()
        self.assertEquals(type(task), RunCounter)
        self.assertEquals(task.min, 4)
        self.assertEquals(task.max, 4)

    def test_order(self):
        mock1 = self.mocker.mock()
        mock2 = self.mocker.mock()
        mock3 = self.mocker.mock()
        mock4 = self.mocker.mock()
        result1 = mock1.attr1(1)
        result2 = mock2.attr2(2)
        result3 = mock3.attr3(3)
        result4 = mock4.attr4(4)

        # Try to spoil the logic which decides which task to reuse.
        other_task = Task()
        for event in self.mocker.get_events():
            event.add_task(other_task)

        self.mocker.order(result1, result2, result3)
        self.mocker.order(result1, result4)
        self.mocker.order(result2, result4)
        events = self.mocker.get_events()
        self.assertEquals(len(events), 8)

        self.assertEquals(events[0].get_tasks(), [other_task])
        other_task_, task1 = events[1].get_tasks()
        self.assertEquals(type(task1), Orderer)
        self.assertEquals(task1.path, events[1].path)
        self.assertEquals(task1.get_dependencies(), [])
        self.assertEquals(other_task_, other_task)

        self.assertEquals(events[2].get_tasks(), [other_task])
        other_task_, task3 = events[3].get_tasks()
        self.assertEquals(type(task3), Orderer)
        self.assertEquals(task3.path, events[3].path)
        self.assertEquals(task3.get_dependencies(), [task1])
        self.assertEquals(other_task_, other_task)

        self.assertEquals(events[4].get_tasks(), [other_task])
        other_task_, task5 = events[5].get_tasks()
        self.assertEquals(type(task5), Orderer)
        self.assertEquals(task5.path, events[5].path)
        self.assertEquals(task5.get_dependencies(), [task3])
        self.assertEquals(other_task_, other_task)

        self.assertEquals(events[6].get_tasks(), [other_task])
        other_task_, task7 = events[7].get_tasks()
        self.assertEquals(type(task7), Orderer)
        self.assertEquals(task7.path, events[7].path)
        self.assertEquals(task7.get_dependencies(), [task1, task3])
        self.assertEquals(other_task_, other_task)

    def test_after(self):
        mock1 = self.mocker.mock()
        mock2 = self.mocker.mock()
        mock3 = self.mocker.mock()
        result1 = mock1.attr1(1)
        result2 = mock2.attr2(2)
        result3 = mock3.attr3(3)

        # Try to spoil the logic which decides which task to reuse.
        other_task = Task()
        for event in self.mocker.get_events():
            event.add_task(other_task)

        self.mocker.after(result1, result2)

        events = self.mocker.get_events()
        self.assertEquals(len(events), 6)

        self.assertEquals(events[0].get_tasks(), [other_task])
        other_task_, task1 = events[1].get_tasks()
        self.assertEquals(type(task1), Orderer)
        self.assertEquals(task1.path, events[1].path)
        self.assertEquals(task1.get_dependencies(), [])
        self.assertEquals(other_task_, other_task)

        self.assertEquals(events[2].get_tasks(), [other_task])
        other_task_, task3 = events[3].get_tasks()
        self.assertEquals(type(task3), Orderer)
        self.assertEquals(task3.path, events[3].path)
        self.assertEquals(task3.get_dependencies(), [])
        self.assertEquals(other_task_, other_task)

        self.assertEquals(events[4].get_tasks(), [other_task])
        other_task_, task5 = events[5].get_tasks()
        self.assertEquals(type(task5), Orderer)
        self.assertEquals(task5.path, events[5].path)
        self.assertEquals(task5.get_dependencies(), [task1, task3])
        self.assertEquals(other_task_, other_task)

    def test_before(self):
        mock1 = self.mocker.mock()
        mock2 = self.mocker.mock()
        mock3 = self.mocker.mock()
        result1 = mock1.attr1(1)
        result2 = mock2.attr2(2)
        result3 = mock3.attr3(3)

        # Try to spoil the logic which decides which task to reuse.
        other_task = Task()
        for event in self.mocker.get_events():
            event.add_task(other_task)

        self.mocker.before(result1, result2)

        events = self.mocker.get_events()
        self.assertEquals(len(events), 6)

        self.assertEquals(events[4].get_tasks(), [other_task])
        other_task_, task5 = events[5].get_tasks()
        self.assertEquals(type(task5), Orderer)
        self.assertEquals(task5.path, events[5].path)
        self.assertEquals(task5.get_dependencies(), [])
        self.assertEquals(other_task_, other_task)

        self.assertEquals(events[0].get_tasks(), [other_task])
        other_task_, task1 = events[1].get_tasks()
        self.assertEquals(type(task1), Orderer)
        self.assertEquals(task1.path, events[1].path)
        self.assertEquals(task1.get_dependencies(), [task5])
        self.assertEquals(other_task_, other_task)

        self.assertEquals(events[2].get_tasks(), [other_task])
        other_task_, task3 = events[3].get_tasks()
        self.assertEquals(type(task3), Orderer)
        self.assertEquals(task3.path, events[3].path)
        self.assertEquals(task3.get_dependencies(), [task5])
        self.assertEquals(other_task_, other_task)

    def test_default_ordering(self):
        self.assertEquals(self.mocker.is_ordering(), False)

    def test_order_without_arguments(self):
        self.mocker.order()
        self.assertEquals(self.mocker.is_ordering(), True)

    def test_order_with_context_manager(self):
        with_manager = self.mocker.order()
        self.assertEquals(self.mocker.is_ordering(), True)
        with_manager.__enter__()
        self.assertEquals(self.mocker.is_ordering(), True)
        with_manager.__exit__(None, None, None)
        self.assertEquals(self.mocker.is_ordering(), False)

    def test_unorder(self):
        self.mocker.order()
        self.mocker.unorder()
        self.assertEquals(self.mocker.is_ordering(), False)

    def test_ordered_events(self):
        mock = self.mocker.mock()

        # Ensure that the state is correctly reset between
        # different ordered blocks.
        self.mocker.order()

        mock.a

        self.mocker.unorder()
        self.mocker.order()

        mock.x.y.z

        events = self.mocker.get_events()

        (task1,) = events[1].get_tasks()
        (task2,) = events[2].get_tasks()
        (task3,) = events[3].get_tasks()

        self.assertEquals(type(task1), Orderer)
        self.assertEquals(type(task2), Orderer)
        self.assertEquals(type(task3), Orderer)

        self.assertEquals(task1.path, events[1].path)
        self.assertEquals(task2.path, events[2].path)
        self.assertEquals(task3.path, events[3].path)

        self.assertEquals(task1.get_dependencies(), [])
        self.assertEquals(task2.get_dependencies(), [task1])
        self.assertEquals(task3.get_dependencies(), [task2])

    def test_nospec(self):
        event1 = self.mocker.add_event(Event())
        event2 = self.mocker.add_event(Event())
        task1 = event1.add_task(SpecChecker(None))
        task2 = event2.add_task(Task())
        task3 = event2.add_task(SpecChecker(None))
        task4 = event2.add_task(Task())
        self.mocker.nospec()
        self.assertEquals(event1.get_tasks(), [task1])
        self.assertEquals(event2.get_tasks(), [task2, task4])

    def test_passthrough(self):
        obj = object()
        mock = self.mocker.proxy(obj)
        event1 = self.mocker.add_event(Event(Path(mock, obj)))
        event2 = self.mocker.add_event(Event(Path(mock, obj)))
        self.mocker.passthrough()
        self.assertEquals(event1.get_tasks(), [])
        (task,) = event2.get_tasks()
        self.assertEquals(type(task), PathExecuter)

    def test_passthrough_fails_on_unproxied(self):
        mock = self.mocker.mock()
        event1 = self.mocker.add_event(Event(Path(mock)))
        event2 = self.mocker.add_event(Event(Path(mock)))
        self.assertRaises(TypeError, self.mocker.passthrough)

    def test_passthrough(self):
        obj = object()
        mock = self.mocker.proxy(obj)
        event = self.mocker.add_event(Event(Path(mock, obj)))
        result_callback = object()
        self.mocker.passthrough(result_callback)
        (task,) = event.get_tasks()
        self.assertEquals(task.get_result_callback(), result_callback)

    def test_on(self):
        obj = self.mocker.mock()
        self.mocker.on(obj.attr).result(123)
        self.mocker.replay()
        self.assertEquals(obj.attr, 123)

    def test_patch(self):
        class C(object): pass
        mock = self.mocker.patch(C)
        self.assertEquals(type(C.__mocker_mock__), Mock)
        self.assertTrue(C.__mocker_mock__ is mock)
        self.assertTrue(mock.__mocker_object__ is C)
        self.assertEquals(type(mock.__mocker_patcher__), Patcher)
        self.assertEquals(mock.__mocker_passthrough__, True)
        self.assertEquals(mock.__mocker_spec__, C)
        (event,) = self.mocker.get_events()
        self.assertEquals(type(event), ReplayRestoreEvent)
        (task,) = event.get_tasks()
        self.assertTrue(task is mock.__mocker_patcher__)

    def test_patch_without_spec(self):
        class C(object): pass
        mock = self.mocker.patch(C, spec=None)
        self.assertEquals(mock.__mocker_spec__, None)

    def test_patch_and_restore_unsets_mocker_mock(self):
        class C(object): pass
        mock = self.mocker.patch(C)
        self.mocker.replay()
        self.assertTrue("__mocker_mock__" in C.__dict__)
        self.mocker.restore()
        self.assertFalse("__mocker_mock__" in C.__dict__)


class ActionTest(TestCase):

    def setUp(self):
        self.mock = Mock(None, name="mock")

    def test_create(self):
        objects = [object() for i in range(4)]
        action = Action(*objects)
        self.assertEquals(action.kind, objects[0])
        self.assertEquals(action.args, objects[1])
        self.assertEquals(action.kwargs, objects[2])
        self.assertEquals(action.path, objects[3])

    def test_repr(self):
        self.assertEquals(repr(Action("kind", "args", "kwargs")),
                          "Action('kind', 'args', 'kwargs')")
        self.assertEquals(repr(Action("kind", "args", "kwargs", "path")),
                          "Action('kind', 'args', 'kwargs', 'path')")

    def test_execute_unknown(self):
        self.assertRaises(RuntimeError, Action("unknown", (), {}).execute, None)

    def test_execute_getattr(self):
        class C(object):
            pass
        obj = C()
        obj.attr = C()
        action = Action("getattr", ("attr",), {})
        self.assertEquals(action.execute(obj), obj.attr)

    def test_execute_setattr(self):
        class C(object):
            pass
        obj = C()
        action = Action("setattr", ("attr", "value"), {})
        action.execute(obj)
        self.assertEquals(getattr(obj, "attr", None), "value")

    def test_execute_delattr(self):
        class C(object):
            pass
        obj = C()
        obj.attr = "value"
        action = Action("delattr", ("attr",), {})
        action.execute(obj)
        self.assertEquals(getattr(obj, "attr", None), None)

    def test_execute_call(self):
        obj = lambda a, b: a+b
        action = Action("call", (1,), {"b": 2})
        self.assertEquals(action.execute(obj), 3)

    def test_execute_contains(self):
        obj = ["a"]
        action = Action("contains", ("a",), {})
        self.assertEquals(action.execute(obj), True)
        action = Action("contains", ("b",), {})
        self.assertEquals(action.execute(obj), False)

    def test_execute_getitem(self):
        obj = {"a": 1}
        action = Action("getitem", ("a",), {})
        self.assertEquals(action.execute(obj), 1)
        action = Action("getitem", ("b",), {})
        self.assertRaises(KeyError, action.execute, obj)

    def test_execute_setitem(self):
        obj = {}
        action = Action("setitem", ("a", 1), {})
        action.execute(obj)
        self.assertEquals(obj, {"a": 1})

    def test_execute_delitem(self):
        obj = {"a": 1, "b": 2}
        action = Action("delitem", ("a",), {})
        action.execute(obj)
        self.assertEquals(obj, {"b": 2})

    def test_execute_len(self):
        obj = [1, 2, 3]
        action = Action("len", (), {})
        self.assertEquals(action.execute(obj), 3)

    def test_execute_nonzero(self):
        obj = []
        action = Action("nonzero", (), {})
        self.assertEquals(action.execute(obj), False)
        obj = [1]
        action = Action("nonzero", (), {})
        self.assertEquals(action.execute(obj), True)

    def test_execute_iter(self):
        obj = [1, 2, 3]
        action = Action("iter", (), {})
        result = action.execute(obj)
        self.assertEquals(type(result), type(iter(obj)))
        self.assertEquals(list(result), obj)

    def test_execute_caching(self):
        values = iter(range(10))
        obj = lambda: values.next()
        action = Action("call", (), {})
        self.assertEquals(action.execute(obj), 0)
        self.assertEquals(action.execute(obj), 0)
        obj = lambda: values.next()
        self.assertEquals(action.execute(obj), 1)

    def test_equals(self):
        obj1 = object()
        obj2 = object()

        self.assertEquals(Action("kind", (), {}, obj1),
                          Action("kind", (), {}, obj2))
        self.assertNotEquals(Action("kind", (), {}, obj1),
                             Action("dnik", (), {}, obj2))
        self.assertNotEquals(Action("kind", (), {}, obj1),
                             Action("kind", (1,), {}, obj2))
        self.assertNotEquals(Action("kind", (), {}, obj1),
                             Action("kind", (), {"a": 1}, obj2))
        self.assertNotEquals(Action("kind", (ANY,), {}, obj1),
                             Action("kind", (1,), {}, obj2))
        self.assertEquals(Action("kind", (CONTAINS(1),), {}, obj1),
                          Action("kind", (CONTAINS(1),), {}, obj2))

    def test_matches(self):
        obj1 = object()
        obj2 = object()

        action1 = Action("kind", (), {}, obj1)
        action2 = Action("kind", (), {}, obj2)
        self.assertTrue(action1.matches(action2))

        action1 = Action("kind", (), {}, obj1)
        action2 = Action("dnik", (), {}, obj2)
        self.assertFalse(action1.matches(action2))

        action1 = Action("kind", (), {}, obj1)
        action2 = Action("kind", (1,), {}, obj2)
        self.assertFalse(action1.matches(action2))

        action1 = Action("kind", (), {}, obj1)
        action2 = Action("kind", (), {"a": 1}, obj2)
        self.assertFalse(action1.matches(action2))

        action1 = Action("kind", (ARGS,), {}, obj1)
        action2 = Action("kind", (), {}, obj2)
        self.assertTrue(action1.matches(action2))

        action1 = Action("kind", (ARGS,), {"a": 1}, obj1)
        action2 = Action("kind", (), {}, obj2)
        self.assertFalse(action1.matches(action2))


class PathTest(TestCase):

    def setUp(self):
        class StubMocker(object):
            def act(self, path):
                pass
        self.mocker = StubMocker()
        self.mock = Mock(self.mocker, name="obj")
        self.object = object()

    def test_create(self):
        mock = object()
        path = Path(mock)
        self.assertEquals(path.root_mock, mock)
        self.assertEquals(path.root_object, None)
        self.assertEquals(path.actions, ())

    def test_create_with_object(self):
        mock = object()
        path = Path(mock, self.object)
        self.assertEquals(path.root_mock, mock)
        self.assertEquals(path.root_object, self.object)

    def test_create_with_actions(self):
        mock = object()
        path = Path(mock, self.object, [1,2,3])
        self.assertEquals(path.root_mock, mock)
        self.assertEquals(path.root_object, self.object)
        self.assertEquals(path.actions, (1,2,3))

    def test_add(self):
        mock = object()
        path = Path(mock, self.object, [1,2,3])
        result = path + 4
        self.assertTrue(result is not path)
        self.assertEquals(result.root_mock, mock)
        self.assertEquals(result.root_object, self.object)
        self.assertEquals(result.actions, (1,2,3,4))

    def test_parent_path(self):
        path1 = Path(self.mock)
        path2 = path1 + Action("getattr", ("attr",), {}, path1)
        path3 = path2 + Action("getattr", ("attr",), {}, path2)

        self.assertEquals(path1.parent_path, None)
        self.assertEquals(path2.parent_path, path1)
        self.assertEquals(path3.parent_path, path2)

    def test_equals(self):
        mock = object()
        obj = object()
        obj1 = object()
        obj2 = object()

        # Not the *same* mock.
        path1 = Path([], obj, [])
        path2 = Path([], obj, [])
        self.assertNotEquals(path1, path2)

        # Not the *same* object.
        path1 = Path(mock, [], [])
        path2 = Path(mock, [], [])
        self.assertNotEquals(path1, path2)

        path1 = Path(mock, obj, [Action("kind", (), {}, obj1)])
        path2 = Path(mock, obj, [Action("kind", (), {}, obj2)])
        self.assertEquals(path1, path2)

        path1 = Path(mock, obj, [Action("kind", (), {}, obj1)])
        path2 = Path(mock, obj, [Action("dnik", (), {}, obj2)])
        self.assertNotEquals(path1, path2)

        path1 = Path(mock, obj, [Action("kind", (), {}, obj1)])
        path2 = Path(object(), obj, [Action("kind", (), {}, obj2)])
        self.assertNotEquals(path1, path2)

        path1 = Path(mock, obj, [Action("kind", (), {}, obj1)])
        path2 = Path(mock, obj, [Action("kind", (1,), {}, obj2)])
        self.assertNotEquals(path1, path2)

        path1 = Path(mock, obj, [Action("kind", (), {}, obj1)])
        path2 = Path(mock, obj, [Action("kind", (), {"a": 1}, obj2)])
        self.assertNotEquals(path1, path2)

        path1 = Path(mock, obj, [Action("kind", (), {}, obj1)])
        path2 = Path(mock, obj, [])
        self.assertNotEquals(path1, path2)

        path1 = Path(mock, obj, [Action("kind", (ANY,), {}, obj1)])
        path2 = Path(mock, obj, [Action("kind", (1,), {}, obj2)])
        self.assertNotEquals(path1, path2)

        path1 = Path(mock, obj, [Action("kind", (CONTAINS(1),), {}, obj1)])
        path2 = Path(mock, obj, [Action("kind", (CONTAINS(1),), {}, obj2)])
        self.assertEquals(path1, path2)

    def test_matches(self):
        obj = object()
        mock = object()
        obj1 = object()
        obj2 = object()

        # Not the *same* mock.
        path1 = Path([], obj, [])
        path2 = Path([], obj, [])
        self.assertFalse(path1.matches(path2))

        path1 = Path(mock, obj1, [])
        path2 = Path(mock, obj2, [])
        self.assertTrue(path1.matches(path2))

        path1 = Path(mock, obj, [Action("kind", (), {}, obj1)])
        path2 = Path(mock, obj, [Action("kind", (), {}, obj2)])
        self.assertTrue(path1.matches(path2))

        path1 = Path(mock, obj, [Action("kind", (), {}, obj1)])
        path2 = Path(mock, obj, [Action("dnik", (), {}, obj2)])
        self.assertFalse(path1.matches(path2))

        path1 = Path(mock, obj, [Action("kind", (), {}, obj1)])
        path2 = Path(object(), [Action("kind", (), {}, obj2)])
        self.assertFalse(path1.matches(path2))

        path1 = Path(mock, obj, [Action("kind", (), {}, obj1)])
        path2 = Path(mock, obj, [Action("kind", (1,), {}, obj2)])
        self.assertFalse(path1.matches(path2))

        path1 = Path(mock, obj, [Action("kind", (), {}, obj1)])
        path2 = Path(mock, obj, [Action("kind", (), {"a": 1}, obj2)])
        self.assertFalse(path1.matches(path2))

        path1 = Path(mock, obj, [Action("kind", (), {}, obj1)])
        path2 = Path(mock, obj, [])
        self.assertFalse(path1.matches(path2))

        path1 = Path(mock, obj, [Action("kind", (ARGS,), {}, obj1)])
        path2 = Path(mock, obj, [Action("kind", (), {}, obj2)])
        self.assertTrue(path1.matches(path2))

        path1 = Path(mock, obj, [Action("kind", (ARGS,), {"a": 1}, obj1)])
        path2 = Path(mock, obj, [Action("kind", (), {}, obj2)])
        self.assertFalse(path1.matches(path2))

    def test_str(self):
        path = Path(self.mock, [])
        self.assertEquals(str(path), "obj")

    def test_str_unnamed(self):
        mock = Mock(self.mocker)
        path = Path(mock, [])
        self.assertEquals(str(path), "<mock>")

    def test_str_auto_named(self):
        named_mock = Mock(self.mocker)
        named_mock.attr
        path = Path(named_mock, [])
        self.assertEquals(str(path), "named_mock")

    def test_str_getattr(self):
        path = Path(self.mock, None, [Action("getattr", ("attr",), {})])
        self.assertEquals(str(path), "obj.attr")

        path += Action("getattr", ("x",), {})
        self.assertEquals(str(path), "obj.attr.x")

    def test_str_getattr_call(self):
        path = Path(self.mock, None, [Action("getattr", ("x",), {}),
                                      Action("getattr", ("y",), {}),
                                      Action("call", ("z",), {})])
        self.assertEquals(str(path), "obj.x.y('z')")

    def test_str_setattr(self):
        path = Path(self.mock, None,
                    [Action("setattr", ("attr", "value"), {})])
        self.assertEquals(str(path), "obj.attr = 'value'")

    def test_str_delattr(self):
        path = Path(self.mock, None, [Action("delattr", ("attr",), {})])
        self.assertEquals(str(path), "del obj.attr")

    def test_str_call(self):
        path = Path(self.mock, None, [Action("call", (), {})])
        self.assertEquals(str(path), "obj()")

        path = Path(self.mock, None,
                    [Action("call", (1, "2"), {"a": 3, "b": "4"})])
        self.assertEquals(str(path), "obj(1, '2', a=3, b='4')")

    def test_str_contains(self):
        path = Path(self.mock, None, [Action("contains", ("value",), {})])
        self.assertEquals(str(path), "'value' in obj")

    def test_str_getitem(self):
        path = Path(self.mock, None, [Action("getitem", ("key",), {})])
        self.assertEquals(str(path), "obj['key']")

    def test_str_setitem(self):
        path = Path(self.mock, None, [Action("setitem", ("key", "value"), {})])
        self.assertEquals(str(path), "obj['key'] = 'value'")

    def test_str_delitem(self):
        path = Path(self.mock, None, [Action("delitem", ("key",), {})])
        self.assertEquals(str(path), "del obj['key']")

    def test_str_len(self):
        path = Path(self.mock, None, [Action("len", (), {})])
        self.assertEquals(str(path), "len(obj)")

    def test_str_nonzero(self):
        path = Path(self.mock, None, [Action("nonzero", (), {})])
        self.assertEquals(str(path), "bool(obj)")

    def test_str_iter(self):
        path = Path(self.mock, None, [Action("iter", (), {})])
        self.assertEquals(str(path), "iter(obj)")

    def test_str_raises_on_unknown(self):
        path = Path(self.mock, None, [Action("unknown", (), {})])
        self.assertRaises(RuntimeError, str, path)

    def test_execute(self):
        class C(object):
            pass
        obj = C()
        obj.x = C()
        obj.x.y = lambda a, b: a+b
        path = Path(self.mock, None, [Action("getattr", ("x",), {}),
                                      Action("getattr", ("y",), {}),
                                      Action("call", (1,), {"b": 2})])
        self.assertEquals(path.execute(obj), 3)


class MatchParamsTest(TestCase):

    def true(self, *args):
        self.assertTrue(match_params(*args), repr(args))

    def false(self, *args):
        self.assertFalse(match_params(*args), repr(args))
    
    def test_any_repr(self):
        self.assertEquals(repr(ANY), "ANY")

    def test_any_equals(self):
        self.assertEquals(ANY, ANY)
        self.assertNotEquals(ANY, ARGS)
        self.assertNotEquals(ANY, object())

    def test_any_matches(self):
        self.assertTrue(ANY.matches(1))
        self.assertTrue(ANY.matches(42))
        self.assertTrue(ANY.matches(object()))

    def test_is_repr(self):
        self.assertEquals(repr(IS("obj")), "IS('obj')")

    def test_is_equals(self):
        l1 = []
        l2 = []
        self.assertNotEquals(IS(l1), l2)
        self.assertEquals(IS(l1), IS(l1))
        self.assertNotEquals(IS(l1), IS(l2))

    def test_is_matches(self):
        l1 = []
        l2 = []
        self.assertTrue(IS(l1).matches(l1))
        self.assertFalse(IS(l1).matches(l2))
        self.assertFalse(IS(l1).matches(ANY))

    def test_contains_repr(self):
        self.assertEquals(repr(CONTAINS("obj")), "CONTAINS('obj')")

    def test_contains_equals(self):
        self.assertEquals(CONTAINS([1]), CONTAINS([1]))
        self.assertNotEquals(CONTAINS(1), CONTAINS([1]))

    def test_contains_matches(self):
        self.assertTrue(CONTAINS(1).matches([1]))
        self.assertFalse(CONTAINS([1]).matches([1]))
        self.assertFalse(CONTAINS(1).matches(object()))

    def test_contains_matches_with_contains(self):
        """Can't be iterated, but has contains hook."""
        class C(object):
            def __contains__(self, value):
                return True
        self.assertTrue(CONTAINS(1).matches(C()))

    def test_in_repr(self):
        self.assertEquals(repr(IN("obj")), "IN('obj')")

    def test_in_equals(self):
        self.assertEquals(IN([1]), IN([1]))
        self.assertNotEquals(IN([1]), IN(1))

    def test_in_matches(self):
        self.assertTrue(IN([1]).matches(1))
        self.assertFalse(IN([1]).matches([1]))
        self.assertFalse(IN([1]).matches(object()))

    def test_match_repr(self):
        self.assertEquals(repr(MATCH("obj")), "MATCH('obj')")

    def test_match_equals(self):
        obj1, obj2 = [], []
        self.assertEquals(MATCH(obj1), MATCH(obj1))
        self.assertNotEquals(MATCH(obj1), MATCH(obj2))

    def test_match_matches(self):
        self.assertTrue(MATCH(lambda x: x > 10).matches(15))
        self.assertFalse(MATCH(lambda x: x > 10).matches(5))

    def test_normal(self):
        self.true((), {}, (), {})
        self.true((1, 2), {"a": 3}, (1, 2), {"a": 3})
        self.false((1,), {}, (), {})
        self.false((), {}, (1,), {})
        self.false((1, 2), {"a": 3}, (1, 2), {"a": 4})
        self.false((1, 2), {"a": 3}, (1, 3), {"a": 3})

    def test_any(self):
        self.true((1, 2), {"a": ANY}, (1, 2), {"a": 4})
        self.true((1, ANY), {"a": 3}, (1, 3), {"a": 3})
        self.false((ANY,), {}, (), {})

    def test_special_args_matching(self):
        self.true((1, IN([2])), {}, (1, 2), {})
        self.true((1, 2), {"a": IN([3])}, (1, 2), {"a": 3})
        self.false((1, IN([2])), {}, (1, 3), {})
        self.false((1, 2), {"a": IN([3])}, (1, 2), {"a": 4})

    def test_args_alone(self):
        self.true((ARGS,), {}, (), {})
        self.true((ARGS,), {}, (1, 2), {})
        self.false((ARGS,), {}, (1, 2), {"a": 2})
        self.false((ARGS,), {}, (), {"a": 2})
        self.true((ARGS,), {"a": 1}, (), {"a": 1})
        self.true((ARGS,), {"a": 1}, (1, 2), {"a": 1})
        self.false((ARGS,), {"a": 1}, (), {"a": 1, "b": 2})
        self.false((ARGS,), {"a": 1}, (1, 2), {"a": 1, "b": 2})
        self.false((ARGS,), {"a": 1}, (), {})

    def test_kwargs_alone(self):
        self.true((KWARGS,), {}, (), {})
        self.false((KWARGS,), {}, (1, 2), {})
        self.false((KWARGS,), {}, (1, 2), {"a": 2})
        self.true((KWARGS,), {}, (), {"a": 2})
        self.true((KWARGS,), {"a": 1}, (), {"a": 1})
        self.false((KWARGS,), {"a": 1}, (1, 2), {"a": 1})
        self.true((KWARGS,), {"a": 1}, (), {"a": 1, "b": 2})
        self.false((KWARGS,), {"a": 1}, (1, 2), {"a": 1, "b": 2})
        self.false((KWARGS,), {"a": 1}, (), {})

    def test_args_kwargs(self):
        self.true((ARGS, KWARGS), {}, (), {})
        self.true((ARGS, KWARGS), {}, (1, 2), {})
        self.true((ARGS, KWARGS), {}, (1, 2), {"a": 2})
        self.true((ARGS, KWARGS), {}, (), {"a": 2})
        self.true((ARGS, KWARGS), {"a": 1}, (), {"a": 1})
        self.true((ARGS, KWARGS), {"a": 1}, (1, 2), {"a": 1})
        self.true((ARGS, KWARGS), {"a": 1}, (), {"a": 1, "b": 2})
        self.true((ARGS, KWARGS), {"a": 1}, (1, 2), {"a": 1, "b": 2})
        self.false((ARGS, KWARGS), {"a": 1}, (), {})

    def test_args_at_start(self):
        self.true((ARGS, 3, 4), {}, (3, 4), {})
        self.true((ARGS, 3, 4), {}, (1, 2, 3, 4), {})
        self.true((ARGS, 3, 4), {"a": 1}, (3, 4), {"a": 1})
        self.false((ARGS, 3, 4), {"a": 1}, (1, 2, 3, 4), {"a": 1, "b": 2})
        self.false((ARGS, 3, 4), {}, (), {})
        self.false((ARGS, 3, 4), {}, (3, 5), {})
        self.false((ARGS, 3, 4), {}, (5, 5), {})
        self.false((ARGS, 3, 4), {}, (3, 4, 5), {})
        self.false((ARGS, 3, 4), {"a": 1}, (), {})
        self.false((ARGS, 3, 4), {"a": 1}, (3, 4), {})
        self.false((ARGS, 3, 4), {"a": 1}, (3, 4), {"b": 2})

    def test_args_at_end(self):
        self.true((1, 2, ARGS), {}, (1, 2), {})
        self.true((1, 2, ARGS), {}, (1, 2, 3, 4), {})
        self.true((1, 2, ARGS), {"a": 1}, (1, 2), {"a": 1})
        self.false((1, 2, ARGS), {"a": 1}, (1, 2, 3, 4), {"a": 1, "b": 2})
        self.false((1, 2, ARGS), {}, (), {})
        self.false((1, 2, ARGS), {}, (1, 3), {})
        self.false((1, 2, ARGS), {}, (3, 3), {})
        self.false((1, 2, ARGS), {"a": 1}, (), {})
        self.false((1, 2, ARGS), {"a": 1}, (1, 2), {})
        self.false((1, 2, ARGS), {"a": 1}, (1, 2), {"b": 2})

    def test_args_at_middle(self):
        self.true((1, ARGS, 4), {}, (1, 4), {})
        self.true((1, ARGS, 4), {}, (1, 2, 3, 4), {})
        self.true((1, ARGS, 4), {"a": 1}, (1, 4), {"a": 1})
        self.false((1, ARGS, 4), {"a": 1}, (1, 2, 3, 4), {"a": 1, "b": 2})
        self.false((1, ARGS, 4), {}, (), {})
        self.false((1, ARGS, 4), {}, (1, 5), {})
        self.false((1, ARGS, 4), {}, (5, 5), {})
        self.false((1, ARGS, 4), {"a": 1}, (), {})
        self.false((1, ARGS, 4), {"a": 1}, (1, 4), {})
        self.false((1, ARGS, 4), {"a": 1}, (1, 4), {"b": 2})

    def test_args_multiple(self):
        self.true((ARGS, 3, ARGS, 6, ARGS), {},
                  (1, 2, 3, 4, 5, 6), {})
        self.true((ARGS, ARGS, ARGS), {}, (1, 2, 3, 4, 5, 6), {})
        self.true((ARGS, ARGS, ARGS), {},  (), {})
        self.false((ARGS, 3, ARGS, 6, ARGS), {},
                   (1, 2, 3, 4, 5), {})
        self.false((ARGS, 3, ARGS, 6, ARGS), {},
                   (1, 2, 4, 5, 6), {})


class MockTest(TestCase):

    def setUp(self):
        self.paths = []
        class StubMocker(object):
            _recording = True
            def is_recording(self):
                return self._recording
            def replay(self):
                self._recording = False
            def act(_, path):
                self.paths.append(path)
                return 42
        self.StubMocker = StubMocker
        self.mocker = StubMocker()
        self.mock = Mock(self.mocker)

    def test_default_attributes(self):
        self.assertEquals(self.mock.__mocker__, self.mocker)
        self.assertEquals(self.mock.__mocker_path__, Path(self.mock))
        self.assertEquals(self.mock.__mocker_name__, None)
        self.assertEquals(self.mock.__mocker_spec__, None)
        self.assertEquals(self.mock.__mocker_type__, None)
        self.assertEquals(self.mock.__mocker_object__, None)
        self.assertEquals(self.mock.__mocker_passthrough__, False)
        self.assertEquals(self.mock.__mocker_patcher__, None)
        self.assertEquals(self.mock.__mocker_replace__, False)
        self.assertEquals(self.mock.__mocker_count__, True)

    def test_path(self):
        path = object()
        self.assertEquals(Mock(self.mocker, path).__mocker_path__, path)

    def test_object(self):
        mock = Mock(self.mocker, object="foo")
        self.assertEquals(mock.__mocker_object__, "foo")
        self.assertEquals(mock.__mocker_path__.root_object, "foo")

    def test_passthrough(self):
        mock = Mock(self.mocker, object="foo", passthrough=True)
        self.assertEquals(mock.__mocker_object__, "foo")
        self.assertEquals(mock.__mocker_passthrough__, True)

    def test_spec(self):
        C = object()
        self.assertEquals(Mock(self.mocker, spec=C).__mocker_spec__, C)

    def test_class_without_type(self):
        mock = Mock(self.mocker)
        self.assertEquals(mock.__class__, Mock)
        self.mocker.replay()
        self.assertEquals(mock.__class__, Mock)

    def test_class_with_type_when_recording(self):
        class C(object): pass
        mock = Mock(self.mocker, type=C)
        self.assertEquals(mock.__mocker_type__, C)
        self.assertEquals(mock.__class__, Mock)
        self.assertEquals(isinstance(mock, Mock), True)

    def test_class_with_type_when_replaying(self):
        class C(object): pass
        mock = Mock(self.mocker, type=C)
        self.mocker.replay()
        self.assertEquals(mock.__mocker_type__, C)
        self.assertEquals(mock.__class__, C)
        self.assertEquals(isinstance(mock, C), True)

    def test_auto_naming(self):
        named_mock = self.mock
        named_mock.attr
        another_name = named_mock
        named_mock = None # Can't find this one anymore.
        another_name.attr
        self.assertEquals(another_name.__mocker_name__, "named_mock")

    def test_auto_naming_on_self(self):
        self.named_mock = self.mock
        del self.mock
        self.named_mock.attr
        self.assertEquals(self.named_mock.__mocker_name__, "named_mock")

    def test_auto_naming_on_bad_self(self):
        self_ = self
        self = object() # No __dict__
        self_.named_mock = self_.mock
        self_.named_mock.attr
        self_.assertEquals(self_.named_mock.__mocker_name__, None)

    def test_auto_naming_without_getframe(self):
        getframe = sys._getframe
        sys._getframe = None
        try:
            self.named_mock = self.mock
            self.named_mock.attr
            self.assertEquals(self.named_mock.__mocker_name__, None)
        finally:
            sys._getframe = getframe

    def test_getattr(self):
        self.assertEquals(self.mock.attr, 42)
        (path,) = self.paths
        self.assertEquals(type(path), Path)
        self.assertTrue(path.parent_path is self.mock.__mocker_path__)
        self.assertEquals(path, self.mock.__mocker_path__ + 
                                Action("getattr", ("attr",), {}))

    def test_setattr(self):
        self.mock.attr = 24
        (path,) = self.paths
        self.assertEquals(type(path), Path)
        self.assertTrue(path.parent_path is self.mock.__mocker_path__)
        self.assertEquals(path, self.mock.__mocker_path__ + 
                                Action("setattr", ("attr", 24), {}))

    def test_delattr(self):
        del self.mock.attr
        (path,) = self.paths
        self.assertEquals(type(path), Path)
        self.assertTrue(path.parent_path is self.mock.__mocker_path__)
        self.assertEquals(path, self.mock.__mocker_path__ + 
                                Action("delattr", ("attr",), {}))

    def test_call(self):
        self.mock(1, a=2)
        (path,) = self.paths
        self.assertEquals(type(path), Path)
        self.assertTrue(path.parent_path is self.mock.__mocker_path__)
        self.assertEquals(path, self.mock.__mocker_path__ + 
                                Action("call", (1,), {"a": 2}))

    def test_contains(self):
        self.assertEquals("value" in self.mock, True) # True due to 42.
        (path,) = self.paths
        self.assertEquals(type(path), Path)
        self.assertTrue(path.parent_path is self.mock.__mocker_path__)
        self.assertEquals(path, self.mock.__mocker_path__ + 
                                Action("contains", ("value",), {}))

    def test_getitem(self):
        self.assertEquals(self.mock["key"], 42)
        (path,) = self.paths
        self.assertEquals(type(path), Path)
        self.assertTrue(path.parent_path is self.mock.__mocker_path__)
        self.assertEquals(path, self.mock.__mocker_path__ + 
                                Action("getitem", ("key",), {}))

    def test_setitem(self):
        self.mock["key"] = "value"
        (path,) = self.paths
        self.assertEquals(type(path), Path)
        self.assertTrue(path.parent_path is self.mock.__mocker_path__)
        self.assertEquals(path, self.mock.__mocker_path__ + 
                                Action("setitem", ("key", "value"), {}))

    def test_delitem(self):
        del self.mock["key"]
        (path,) = self.paths
        self.assertEquals(type(path), Path)
        self.assertTrue(path.parent_path is self.mock.__mocker_path__)
        self.assertEquals(path, self.mock.__mocker_path__ + 
                                Action("delitem", ("key",), {}))

    def test_len(self):
        self.assertEquals(len(self.mock), 42)
        (path,) = self.paths
        self.assertEquals(type(path), Path)
        self.assertTrue(path.parent_path is self.mock.__mocker_path__)
        self.assertEquals(path, self.mock.__mocker_path__ + 
                                Action("len", (), {}))

    def test_len_with_mock_result(self):
        self.mocker.act = lambda path: Mock(self.mocker)
        self.assertEquals(len(self.mock), 0)

    def test_len_transforms_match_error_to_attribute_error(self):
        """
        list() uses len() as a hint. When we mock iter(), it shouldn't
        explode due to the lack of len().
        """
        def raise_error(path):
            raise MatchError("Kaboom!")

        self.mocker.act = raise_error
        try:
            len(self.mock)
        except AttributeError, e:
            self.assertEquals(str(e), "Kaboom!")
        except MatchError:
            self.fail("Expected AttributeError, not MatchError.")
        else:
            self.fail("AttributeError not raised.")

    def test_mock_raises_attribute_error_on_length_hint(self):
        """
        In Python 2.6+ list() uses __length_hint__() as a hint. When
        we mock iter(), it shouldn't explode due to the lack of
        __length_hint__.
        """
        def raise_error(path):
            raise MatchError("Kaboom!")

        self.mocker.act = raise_error
        try:
            self.mock.__length_hint__
        except AttributeError, e:
            self.assertEquals(str(e), "No __length_hint__ here!")
        except MatchError:
            self.fail("Expected AttributeError, not MatchError.")
        else:
            self.fail("AttributeError not raised.")

    def test_nonzero(self):
        self.assertEquals(bool(self.mock), True) # True due to 42.
        (path,) = self.paths
        self.assertEquals(type(path), Path)
        self.assertTrue(path.parent_path is self.mock.__mocker_path__)
        self.assertEquals(path, self.mock.__mocker_path__ + 
                                Action("nonzero", (), {}))

    def test_nonzero_returns_true_on_match_error(self):
        """
        When an object doesn't define a boolean behavior explicitly, it
        should be handled as a true value by default, as Python usually
        does.
        """
        def raise_error(path):
            raise MatchError("Kaboom!")
        self.mocker.act = raise_error
        self.assertEquals(bool(self.mock), True)

    def test_nonzero_with_mock_result(self):
        self.mocker.act = lambda path: Mock(self.mocker)
        self.assertEquals(bool(self.mock), True)

    def test_iter(self):
        result_mock = Mock(self.mocker)
        self.mocker.act = lambda path: self.paths.append(path) or result_mock
        result = iter(self.mock)
        self.assertEquals(type(result), type(iter([])))
        self.assertEquals(list(result), [])
        (path,) = self.paths
        self.assertEquals(type(path), Path)
        self.assertTrue(path.parent_path is self.mock.__mocker_path__)
        self.assertEquals(path, self.mock.__mocker_path__ + 
                                Action("iter", (), {}))

    def test_passthrough_on_unexpected(self):
        class StubMocker(object):
            def act(self, path):
                if path.actions[-1].args == ("x",):
                    raise MatchError
                return 42
        class C(object):
            x = 123
            y = 321

        mock = Mock(StubMocker(), object=C())
        self.assertRaises(MatchError, getattr, mock, "x", 42)
        self.assertEquals(mock.y, 42)

        mock = Mock(StubMocker(), passthrough=True)
        self.assertRaises(MatchError, getattr, mock, "x", 42)
        self.assertEquals(mock.y, 42)

        mock = Mock(StubMocker(), object=C(), passthrough=True)
        self.assertEquals(mock.x, 123)
        self.assertEquals(mock.y, 42)

        mock = Mock(StubMocker(), passthrough=True)
        act = mock.__mocker_act__
        self.assertEquals(act("getattr", ("x",), 42, object=C()), 123)
        self.assertEquals(act("getattr", ("y",), 42, object=C()), 42)

    def test_act_with_object(self):
        obj = object()
        self.mock.__mocker_act__("kind", object=obj)
        (path,) = self.paths
        self.assertEquals(type(path), Path)
        self.assertTrue(path.parent_path is self.mock.__mocker_path__)
        self.assertTrue(path.root_object is obj)

    def test_reraise_assertion(self):
        class StubMocker(object):
            def act(self, path):
                message = os.linesep.join(["An", "- error", "- happened"])
                raise AssertionError(message)
        mock = Mock(StubMocker())
        try:
            mock.__mocker_act__("kind")
        except AssertionError, e:
            message = os.linesep.join(["[Mocker] Unmet expectation:",
                                       "",
                                       "=> An",
                                       " - error",
                                       " - happened",
                                       ""])
            self.assertEquals(str(e), message)
        else:
            self.fail("AssertionError not raised")

    def test_action_execute_and_path_str(self):
        """Check for kind support on Action.execute() and Path.__str__()."""
        mocker = Mocker()
        check = []
        for name, attr in Mock.__dict__.iteritems():
            if not name.startswith("__mocker_") and hasattr(attr, "__call__"):
                mock = mocker.mock()
                args = ["arg"] * (attr.func_code.co_argcount - 1)
                try:
                    attr(mock, *args)
                except:
                    pass
                else:
                    path = mocker.get_events()[-1].path
                    check.append((path, path.actions[-1]))

        for path, action in check:
            kind = action.kind

            try:
                str(path)
            except RuntimeError:
                self.fail("Kind %r not supported by Path.__str__()" % kind)

            try:
                action.execute(object())
            except RuntimeError:
                self.fail("Kind %r not supported by Action.execute()" % kind)
            except:
                pass


class EventTest(TestCase):

    def setUp(self):
        self.event = Event()

    def test_default_path(self):
        self.assertEquals(self.event.path, None)

    def test_path(self):
        path = object()
        event = Event(path)
        self.assertEquals(event.path, path)

    def test_add_and_get_tasks(self):
        task1 = self.event.add_task(Task())
        task2 = self.event.add_task(Task())
        self.assertEquals(self.event.get_tasks(), [task1, task2])

    def test_remove_task(self):
        task1 = self.event.add_task(Task())
        task2 = self.event.add_task(Task())
        task3 = self.event.add_task(Task())
        self.event.remove_task(task2)
        self.assertEquals(self.event.get_tasks(), [task1, task3])

    def test_default_matches(self):
        self.assertEquals(self.event.matches(None), False)

    def test_default_run(self):
        self.assertEquals(self.event.run(None), None)

    def test_default_satisfied(self):
        self.assertEquals(self.event.satisfied(), True)

    def test_default_verify(self):
        self.assertEquals(self.event.verify(), None)

    def test_default_replay(self):
        self.assertEquals(self.event.replay(), None)

    def test_default_restore(self):
        self.assertEquals(self.event.restore(), None)

    def test_matches_false(self):
        task1 = self.event.add_task(Task())
        task1.matches = lambda path: True
        task2 = self.event.add_task(Task())
        task2.matches = lambda path: False
        task3 = self.event.add_task(Task())
        task3.matches = lambda path: True
        self.assertEquals(self.event.matches(None), False)

    def test_matches_true(self):
        task1 = self.event.add_task(Task())
        task1.matches = lambda path: True
        task2 = self.event.add_task(Task())
        task2.matches = lambda path: True
        self.assertEquals(self.event.matches(None), True)

    def test_matches_argument(self):
        calls = []
        task = self.event.add_task(Task())
        task.matches = lambda path: calls.append(path)
        self.event.matches(42)
        self.assertEquals(calls, [42])

    def test_run(self):
        calls = []
        task1 = self.event.add_task(Task())
        task1.run = lambda path: calls.append(path) or True
        task2 = self.event.add_task(Task())
        task2.run = lambda path: calls.append(path) or False
        task3 = self.event.add_task(Task())
        task3.run = lambda path: calls.append(path) or None
        self.assertEquals(self.event.run(42), False)
        self.assertEquals(calls, [42, 42, 42])

    def test_run_errors(self):
        class MyTask(object):
            def __init__(self, id, failed):
                self.id = id
                self.failed = failed
            def run(self, path):
                if self.failed:
                    raise AssertionError("%d failed" % self.id)
        event = Event("i.am.a.path")
        event.add_task(MyTask(1, True))
        event.add_task(MyTask(2, False))
        event.add_task(MyTask(3, True))

        try:
            event.run("i.am.a.path")
        except AssertionError, e:
            message = os.linesep.join(["i.am.a.path",
                                       "- 1 failed",
                                       "- 3 failed"])
            self.assertEquals(str(e), message)
        else:
            self.fail("AssertionError not raised")

    def test_run_errors_with_different_path_representation(self):
        """When the path representation isn't the same it's shown up."""
        class MyTask(object):
            def __init__(self, id, failed):
                self.id = id
                self.failed = failed
            def run(self, path):
                if self.failed:
                    raise AssertionError("%d failed" % self.id)
        event = Event("i.am.a.path")
        event.add_task(MyTask(1, True))
        event.add_task(MyTask(2, False))
        event.add_task(MyTask(3, True))

        try:
            event.run(42)
        except AssertionError, e:
            message = os.linesep.join(["i.am.a.path",
                                       "- Run: 42", # <==
                                       "- 1 failed",
                                       "- 3 failed"])
            self.assertEquals(str(e), message)
        else:
            self.fail("AssertionError not raised")

    def test_run_errors_need_good_messages(self):
        class MyTask(Task):
            def run(self, path):
                raise AssertionError()
        self.event.add_task(MyTask())
        self.assertRaises(RuntimeError, self.event.run, 42)

    def test_has_run(self):
        self.assertFalse(self.event.has_run())
        self.event.run(None)
        self.assertTrue(self.event.has_run())

    def test_has_run_reset_on_replay(self):
        self.event.run(None)
        self.event.replay()
        self.assertFalse(self.event.has_run())

    def test_may_run(self):
        calls = []
        task1 = Task()
        task1.may_run = lambda path: calls.append((1, path)) or True
        task2 = Task()
        task2.may_run = lambda path: calls.append((2, path))

        self.assertEquals(self.event.may_run(42), True)

        self.event.add_task(task1)
        self.assertEquals(self.event.may_run(42), True)
        self.assertEquals(calls, [(1, 42)])

        del calls[:]
        self.event.add_task(task2)
        self.event.add_task(task1) # Should return on first false.
        self.assertEquals(self.event.may_run(42), False)
        self.assertEquals(calls, [(1, 42), (2, 42)])

    def test_satisfied_false(self):
        def raise_error():
            raise AssertionError("An error")
        task1 = self.event.add_task(Task())
        task2 = self.event.add_task(Task())
        task2.verify = raise_error
        task3 = self.event.add_task(Task())
        self.assertEquals(self.event.satisfied(), False)

    def test_satisfied_true(self):
        task1 = self.event.add_task(Task())
        task1.satisfied = lambda: True
        task2 = self.event.add_task(Task())
        task2.satisfied = lambda: True
        self.assertEquals(self.event.satisfied(), True)

    def test_verify(self):
        class MyTask(object):
            def __init__(self, id, failed):
                self.id = id
                self.failed = failed
            def verify(self):
                if self.failed:
                    raise AssertionError("%d failed" % self.id)
        event = Event("i.am.a.path")
        event.add_task(MyTask(1, True))
        event.add_task(MyTask(2, False))
        event.add_task(MyTask(3, True))

        try:
            event.verify()
        except AssertionError, e:
            message = os.linesep.join(["i.am.a.path",
                                       "- 1 failed",
                                       "- 3 failed"])
            self.assertEquals(str(e), message)
        else:
            self.fail("AssertionError not raised")

    def test_verify_errors_need_good_messages(self):
        class MyTask(Task):
            def verify(self):
                raise AssertionError()
        self.event.add_task(MyTask())
        self.assertRaises(RuntimeError, self.event.verify)

    def test_replay(self):
        calls = []
        task1 = self.event.add_task(Task())
        task2 = self.event.add_task(Task())
        task1.replay = lambda: calls.append("task1")
        task2.replay = lambda: calls.append("task2")
        self.event.replay()
        self.assertEquals(calls, ["task1", "task2"])

    def test_restore(self):
        calls = []
        task1 = self.event.add_task(Task())
        task2 = self.event.add_task(Task())
        task1.restore = lambda: calls.append("task1")
        task2.restore = lambda: calls.append("task2")
        self.event.restore()
        self.assertEquals(calls, ["task1", "task2"])


class ReplayRestoreEventTest(TestCase):

    def setUp(self):
        self.event = ReplayRestoreEvent()

    def test_never_matches(self):
        self.assertEquals(self.event.matches(None), False)
        self.event.add_task(Task())
        self.assertEquals(self.event.matches(None), False)


class TaskTest(TestCase):

    def setUp(self):
        self.task = Task()

    def test_default_matches(self):
        self.assertEquals(self.task.matches(None), True)

    def test_default_may_run(self):
        self.assertEquals(self.task.may_run(None), True)

    def test_default_run(self):
        self.assertEquals(self.task.run(None), None)

    def test_default_verify(self):
        self.assertEquals(self.task.verify(), None)

    def test_default_replay(self):
        self.assertEquals(self.task.replay(), None)

    def test_default_restore(self):
        self.assertEquals(self.task.restore(), None)


class OnRestoreCallerTest(TestCase):

    def setUp(self):
        self.mocker = CleanMocker()
        self.mock = self.mocker.mock()

    def test_is_task(self):
        self.assertTrue(isinstance(OnRestoreCaller(None), Task))

    def test_restore(self):
        calls = []
        task = OnRestoreCaller(lambda: calls.append("callback"))
        self.assertEquals(calls, [])
        task.restore()
        self.assertEquals(calls, ["callback"])
        task.restore()
        self.assertEquals(calls, ["callback", "callback"])


class PathMatcherTest(TestCase):

    def setUp(self):
        self.mocker = CleanMocker()
        self.mock = self.mocker.mock()

    def test_is_task(self):
        self.assertTrue(isinstance(PathMatcher(None), Task))

    def test_create(self):
        path = object()
        task = PathMatcher(path)
        self.assertEquals(task.path, path)

    def test_matches(self):
        path = Path(self.mock, None, [Action("getattr", ("attr1",), {})])
        task = PathMatcher(path)
        action = Action("getattr", (), {}, Path(self.mock))
        self.assertFalse(task.matches(action.path + action))
        action = Action("getattr", ("attr1",), {}, Path(self.mock))
        self.assertTrue(task.matches(action.path + action))

    def test_recorder(self):
        path = Path(self.mock, [Action("call", (), {})])
        event = Event(path)
        path_matcher_recorder(self.mocker, event)
        (task,) = event.get_tasks()
        self.assertEquals(type(task), PathMatcher)
        self.assertTrue(task.path is path)

    def test_is_standard_recorder(self):
        self.assertTrue(path_matcher_recorder in Mocker.get_recorders())


class RunCounterTest(TestCase):

    def setUp(self):
        self.mocker = CleanMocker()
        self.mock = self.mocker.mock()
        self.action = Action("getattr", ("attr",), {}, Path(self.mock))
        self.path = Path(self.mock, [self.action])
        self.event = Event(self.path)

    def test_is_task(self):
        self.assertTrue(isinstance(RunCounter(1), Task))

    def test_create_one_argument(self):
        task = RunCounter(2)
        self.assertEquals(task.min, 2)
        self.assertEquals(task.max, 2)

    def test_create_min_max(self):
        task = RunCounter(2, 3)
        self.assertEquals(task.min, 2)
        self.assertEquals(task.max, 3)

    def test_create_unbounded(self):
        task = RunCounter(2, None)
        self.assertEquals(task.min, 2)
        self.assertEquals(task.max, sys.maxint)

    def test_run_one_argument(self):
        task = RunCounter(2)
        task.run(self.path)
        task.run(self.path)
        self.assertRaises(AssertionError, task.run, self.path)

    def test_run_two_arguments(self):
        task = RunCounter(1, 2)
        task.run(self.path)
        task.run(self.path)
        self.assertRaises(AssertionError, task.run, self.path)

    def test_may_run(self):
        task = RunCounter(1)
        self.assertEquals(task.may_run(None), True)
        task.run(self.path)
        self.assertEquals(task.may_run(None), False)

    def test_verify(self):
        task = RunCounter(2)
        self.assertRaises(AssertionError, task.verify)
        task.run(self.path)
        self.assertRaises(AssertionError, task.verify)
        task.run(self.path)
        task.verify()
        self.assertRaises(AssertionError, task.run, self.path)
        self.assertRaises(AssertionError, task.verify)

    def test_verify_two_arguments(self):
        task = RunCounter(1, 2)
        self.assertRaises(AssertionError, task.verify)
        task.run(self.path)
        task.verify()
        task.run(self.path)
        task.verify()
        self.assertRaises(AssertionError, task.run, self.path)
        self.assertRaises(AssertionError, task.verify)

    def test_verify_unbound(self):
        task = RunCounter(1, None)
        self.assertRaises(AssertionError, task.verify)
        task.run(self.path)
        task.verify()
        task.run(self.path)
        task.verify()

    def test_reset_on_replay(self):
        task = RunCounter(1, 1)
        task.run(self.path)
        self.assertRaises(AssertionError, task.run, self.path)
        task.replay()
        self.assertRaises(AssertionError, task.verify)
        task.run(self.path)
        self.assertRaises(AssertionError, task.run, self.path)

    def test_recorder(self):
        run_counter_recorder(self.mocker, self.event)
        (task,) = self.event.get_tasks()
        self.assertEquals(type(task), ImplicitRunCounter)
        self.assertTrue(task.min == 1)
        self.assertTrue(task.max == 1)

    def test_recorder_wont_record_when_count_is_false(self):
        self.mock.__mocker_count__ = False
        run_counter_recorder(self.mocker, self.event)
        self.assertEquals(self.event.get_tasks(), [])

    def test_removal_recorder(self):
        """
        Events created by getattr actions which lead to other events
        may be repeated any number of times.
        """
        path1 = Path(self.mock)
        path2 = path1 + Action("getattr", ("attr",), {}, path1)
        path3 = path2 + Action("getattr", ("attr",), {}, path2)
        path4 = path3 + Action("call", (), {}, path3)
        path5 = path4 + Action("call", (), {}, path4)

        event3 = self.mocker.add_event(Event(path3))
        event2 = self.mocker.add_event(Event(path2))
        event5 = self.mocker.add_event(Event(path5))
        event4 = self.mocker.add_event(Event(path4))

        event2.add_task(RunCounter(1))
        event2.add_task(ImplicitRunCounter(1))
        event2.add_task(RunCounter(1))
        event3.add_task(RunCounter(1))
        event3.add_task(ImplicitRunCounter(1))
        event3.add_task(RunCounter(1))
        event4.add_task(RunCounter(1))
        event4.add_task(ImplicitRunCounter(1))
        event4.add_task(RunCounter(1))
        event5.add_task(RunCounter(1))
        event5.add_task(ImplicitRunCounter(1))
        event5.add_task(RunCounter(1))
        
        # First, when the previous event isn't a getattr.

        run_counter_removal_recorder(self.mocker, event5)

        self.assertEquals(len(event2.get_tasks()), 3)
        self.assertEquals(len(event3.get_tasks()), 3)
        self.assertEquals(len(event4.get_tasks()), 3)
        self.assertEquals(len(event5.get_tasks()), 3)

        # Now, for real.

        run_counter_removal_recorder(self.mocker, event4)

        self.assertEquals(len(event2.get_tasks()), 3)
        self.assertEquals(len(event3.get_tasks()), 2)
        self.assertEquals(len(event4.get_tasks()), 3)
        self.assertEquals(len(event5.get_tasks()), 3)

        task1, task2 = event3.get_tasks()
        self.assertEquals(type(task1), RunCounter)
        self.assertEquals(type(task2), RunCounter)

    def test_removal_recorder_with_obj(self):

        self.mocker.add_recorder(run_counter_recorder)
        self.mocker.add_recorder(run_counter_removal_recorder)

        obj = self.mocker.mock()

        obj.x.y()()

        events = self.mocker.get_events()
        self.assertEquals(len(events), 4)
        self.assertEquals(len(events[0].get_tasks()), 0)
        self.assertEquals(len(events[1].get_tasks()), 0)
        self.assertEquals(len(events[2].get_tasks()), 1)
        self.assertEquals(len(events[3].get_tasks()), 1)

    def test_reset_on_replay_with_mock(self):
        mock = self.mocker.mock()
        mock()
        self.mocker.count(1)
        self.mocker.replay()
        mock()
        self.assertRaises(AssertionError, mock)
        self.mocker.replay()
        mock()
        self.assertRaises(AssertionError, mock)

    def test_is_standard_recorder(self):
        self.assertTrue(run_counter_recorder in Mocker.get_recorders())
        self.assertTrue(run_counter_removal_recorder in Mocker.get_recorders())


class MockReturnerTest(TestCase):

    def setUp(self):
        self.mocker = CleanMocker()
        self.mock = self.mocker.mock()
        self.action = Action("getattr", ("attr",), {}, Path(self.mock))
        self.path = Path(self.mock, [self.action])
        self.event = Event(self.path)

    def test_is_task(self):
        self.assertTrue(isinstance(MockReturner(self.mocker), Task))

    def test_create(self):
        task = MockReturner(self.mocker)
        mock = task.run(self.path)
        self.assertTrue(isinstance(mock, Mock))
        self.assertEquals(mock.__mocker__, self.mocker)
        self.assertTrue(mock.__mocker_path__.matches(self.path))

    def test_recorder(self):
        path1 = Path(self.mock)
        path2 = path1 + Action("getattr", ("attr",), {}, path1)
        path3 = path2 + Action("getattr", ("attr",), {}, path2)
        path4 = path3 + Action("call", (), {}, path3)

        event2 = self.mocker.add_event(Event(path2))
        event3 = self.mocker.add_event(Event(path3))
        event4 = self.mocker.add_event(Event(path4))

        self.assertEquals(len(event2.get_tasks()), 0)
        self.assertEquals(len(event3.get_tasks()), 0)
        self.assertEquals(len(event4.get_tasks()), 0)

        # Calling on 4 should add it only to the parent.

        mock_returner_recorder(self.mocker, event4)

        self.assertEquals(len(event2.get_tasks()), 0)
        self.assertEquals(len(event3.get_tasks()), 1)
        self.assertEquals(len(event4.get_tasks()), 0)

        (task,) = event3.get_tasks()
        self.assertEquals(type(task), MockReturner)
        self.assertEquals(task.mocker, self.mocker)

        # Calling on it again shouldn't do anything.

        mock_returner_recorder(self.mocker, event4)

        self.assertEquals(len(event2.get_tasks()), 0)
        self.assertEquals(len(event3.get_tasks()), 1)
        self.assertEquals(len(event4.get_tasks()), 0)

    def test_is_standard_recorder(self):
        self.assertTrue(mock_returner_recorder in Mocker.get_recorders())


class FunctionRunnerTest(TestCase):

    def setUp(self):
        self.mocker = CleanMocker()
        self.mock = self.mocker.mock()
        self.action = Action("call", (1, 2), {"c": 3}, Path(self.mock))
        self.path = Path(self.mock, None, [self.action])
        self.event = Event(self.path)

    def test_is_task(self):
        self.assertTrue(isinstance(FunctionRunner(None), Task))

    def test_run(self):
        task = FunctionRunner(lambda *args, **kwargs: repr((args, kwargs)))
        result = task.run(self.path)
        self.assertEquals(result, "((1, 2), {'c': 3})")


class PathExecuterTest(TestCase):

    def setUp(self):
        self.mocker = CleanMocker()

    def test_is_task(self):
        self.assertTrue(isinstance(PathExecuter(), Task))

    def test_run(self):
        class C(object):
            pass
        obj = C()
        obj.x = C()
        obj.x.y = lambda a, b: a+b

        path = Path(None, obj, [Action("getattr", ("x",), {}),
                                Action("getattr", ("y",), {}),
                                Action("call", (1,), {"b": 2})])

        task = PathExecuter()
        self.assertEquals(task.run(path), 3)

    def test_run_with_result_callback(self):
        class C(object):
            def x(self, arg):
                return 41 + arg
        obj = C()

        path = Path(None, obj, [Action("getattr", ("x",), {}),
                                Action("call", (1,), {})])

        calls = []
        result_callback = lambda result: calls.append(result)
        task = PathExecuter(result_callback)
        self.assertEquals(task.get_result_callback(), result_callback)
        self.assertEquals(task.run(path), 42)
        self.assertEquals(calls, [42])


class OrdererTest(TestCase):

    def setUp(self):
        self.mocker = CleanMocker()
        self.mock = self.mocker.mock()
        self.action = Action("call", (1, 2, Path(self.mock)), {"c": 3})
        self.path = Path(self.mock, [self.action])

    def test_is_task(self):
        self.assertTrue(isinstance(Orderer(self.path), Task))

    def test_path(self):
        self.assertEquals(Orderer(self.path).path, self.path)

    def test_has_run(self):
        orderer = Orderer(self.path)
        self.assertFalse(orderer.has_run())
        orderer.run(self.path)
        self.assertTrue(orderer.has_run())

    def test_reset_on_replay(self):
        orderer = Orderer(self.path)
        orderer.run(self.path)
        orderer.replay()
        self.assertFalse(orderer.has_run())

    def test_reset_on_replay_with_mock(self):
        self.mocker.add_recorder(path_matcher_recorder)
        mock = self.mocker.mock()
        self.mocker.order(mock(1), mock(2))
        self.mocker.replay()
        mock(1)
        mock(2)
        self.mocker.replay()
        self.assertRaises(AssertionError, mock, 2)

    def test_add_and_get_dependencies(self):
        orderer = Orderer(self.path)
        orderer.add_dependency(1)
        orderer.add_dependency(2)
        self.assertEquals(orderer.get_dependencies(), [1, 2])

    def test_may_run(self):
        orderer1 = Orderer(self.path)
        orderer2 = Orderer(self.path)
        orderer2.add_dependency(orderer1)
        self.assertFalse(orderer2.may_run(None))
        self.assertTrue(orderer1.may_run(None))
        orderer1.run(self.path)
        self.assertTrue(orderer2.may_run(None))

    def test_run_with_missing_dependency(self):
        orderer1 = Orderer("path1")
        orderer2 = Orderer("path2")
        orderer2.add_dependency(orderer1)
        try:
            orderer2.run(None)
        except AssertionError, e:
            self.assertEquals(str(e), "Should be after: path1")
        else:
            self.fail("AssertionError not raised")


class SpecCheckerTest(TestCase):

    def setUp(self):
        class C(object):
            def __call__(self, a, b, c=3): pass
            def normal(self, a, b, c=3): pass
            def varargs(self, a, b, c=3, *args): pass
            def varkwargs(self, a, b, c=3, **kwargs): pass
            def varargskwargs(self, a, b, c=3, *args, **kwargs): pass
            def klass(cls, a, b, c=3): pass
            klass = classmethod(klass)
            def static(a, b, c=3): pass
            static = staticmethod(static)
            def noargs(self): pass
            def klassnoargs(cls): pass
            klassnoargs = classmethod(klassnoargs)
            def staticnoargs(): pass
            staticnoargs = staticmethod(staticnoargs)
        self.cls = C
        self.mocker = CleanMocker()
        self.mock = self.mocker.mock(self.cls)

    def path(self, *args, **kwargs):
        action = Action("call", args, kwargs, Path(self.mock))
        return action.path + action

    def good(self, method_names, args_expr):
        if type(method_names) is not list:
            method_names = [method_names]
        for method_name in method_names:
            task = SpecChecker(getattr(self.cls, method_name, None))
            path = eval("self.path(%s)" % args_expr)
            self.assertEquals(task.may_run(path), True)
            try:
                task.run(path)
            except AssertionError:
                self.fail("AssertionError raised with self.cls.%s(%s)"
                          % (method_name, args_expr))

    def bad(self, method_names, args_expr):
        if type(method_names) is not list:
            method_names = [method_names]
        for method_name in method_names:
            task = SpecChecker(getattr(self.cls, method_name, None))
            path = eval("self.path(%s)" % args_expr)
            self.assertEquals(task.may_run(path), False)
            try:
                task.run(path)
            except AssertionError:
                pass
            else:
                self.fail("AssertionError not raised with self.cls.%s(%s)"
                          % (method_name, args_expr))

    def test_get_method(self):
        task = SpecChecker(self.cls.noargs)
        self.assertEquals(task.get_method(), self.cls.noargs)

    def test_is_standard_recorder(self):
        self.assertTrue(spec_checker_recorder in Mocker.get_recorders())

    def test_is_task(self):
        self.assertTrue(isinstance(SpecChecker(self.cls.normal), Task))

    def test_error_message(self):
        task = SpecChecker(self.cls.normal)
        try:
            task.run(self.path(1))
        except AssertionError, e:
            self.assertEquals(str(e), "Specification is normal(a, b, c=3): "
                                      "'b' not provided")
        else:
            self.fail("AssertionError not raised")

    def test_verify_unexistent_method(self):
        task = SpecChecker(None)
        try:
            task.verify()
        except AssertionError, e:
            self.assertEquals(str(e), "Method not found in real specification")
        else:
            self.fail("AssertionError not raised")

    def test_unsupported_object_for_getargspec(self):
        from zlib import adler32
        # If that fails, this test has to change because either adler32 has
        # changed, or the implementation of getargspec has changed.
        self.assertRaises(TypeError, inspect.getargspec, adler32)
        try:
            task = SpecChecker(adler32)
            task.run(self.path("asd"))
        except TypeError, e:
            self.fail("TypeError: %s" % str(e))

    def test_recorder(self):
        self.mocker.add_recorder(spec_checker_recorder)
        obj = self.mocker.mock(spec=self.cls)
        obj.noargs()
        getattr, call = self.mocker.get_events()
        self.assertEquals(getattr.get_tasks(), [])
        (task,) = call.get_tasks()
        self.assertEquals(type(task), SpecChecker)
        self.assertEquals(task.get_method(), self.cls.noargs)

    def test_recorder_with_unexistent_method(self):
        self.mocker.add_recorder(spec_checker_recorder)
        obj = self.mocker.mock(spec=self.cls)
        obj.unexistent()
        getattr, call = self.mocker.get_events()
        self.assertEquals(getattr.get_tasks(), [])
        (task,) = call.get_tasks()
        self.assertEquals(type(task), SpecChecker)
        self.assertEquals(task.get_method(), None)

    def test_recorder_second_action_isnt_call(self):
        self.mocker.add_recorder(spec_checker_recorder)
        obj = self.mocker.mock(spec=self.cls)
        obj.noargs.x
        event1, event2 = self.mocker.get_events()
        self.assertEquals(event1.get_tasks(), [])
        self.assertEquals(event2.get_tasks(), [])

    def test_recorder_first_action_isnt_getattr(self):
        self.mocker.add_recorder(spec_checker_recorder)
        obj = self.mocker.mock(spec=self.cls)
        obj.__mocker_act__("anyother", ("attr",))()
        event1, event2 = self.mocker.get_events()
        self.assertEquals(event1.get_tasks(), [])
        self.assertEquals(event2.get_tasks(), [])

    def test_recorder_more_than_two_actions(self):
        self.mocker.add_recorder(spec_checker_recorder)
        obj = self.mocker.mock(spec=self.cls)
        obj.noargs().x
        event1, event2, event3 = self.mocker.get_events()
        self.assertEquals(len(event1.get_tasks()), 0)
        self.assertEquals(len(event2.get_tasks()), 1)
        self.assertEquals(len(event3.get_tasks()), 0)

    def test_recorder_with_call_on_object(self):
        self.mocker.add_recorder(spec_checker_recorder)
        obj = self.mocker.mock(spec=self.cls)
        obj()
        (call,) = self.mocker.get_events()
        (task,) = call.get_tasks()
        self.assertEquals(type(task), SpecChecker)
        self.assertEquals(task.get_method(), self.cls.__call__)

    def test_recorder_more_than_one_action_with_direct_call(self):
        self.mocker.add_recorder(spec_checker_recorder)
        obj = self.mocker.mock(spec=self.cls)
        obj().x
        event1, event2 = self.mocker.get_events()
        self.assertEquals(len(event1.get_tasks()), 1)
        self.assertEquals(len(event2.get_tasks()), 0)

    def test_noargs(self):
        methods = ["noargs", "klassnoargs", "staticnoargs"]
        self.good(methods, "")
        self.bad(methods, "1")
        self.bad(methods, "a=1")

    def test_args_and_kwargs(self):
        methods = ["__call__", "normal", "varargs", "varkwargs",
                   "varargskwargs", "static", "klass"]
        self.good(methods, "1, 2")
        self.good(methods, "1, 2, 3")
        self.good(methods, "1, b=2")
        self.good(methods, "1, b=2, c=3")
        self.good(methods, "a=1, b=2")
        self.good(methods, "a=1, b=2, c=3")

    def test_too_much(self):
        methods = ["__call__", "normal", "static", "klass"]
        self.bad(methods, "1, 2, 3, 4")
        self.bad(methods, "1, 2, d=4")

    def test_missing(self):
        methods = ["__call__", "normal", "varargs", "varkwargs",
                   "varargskwargs", "static", "klass"]
        self.bad(methods, "")
        self.bad(methods, "1")
        self.bad(methods, "c=3")
        self.bad(methods, "a=1")
        self.bad(methods, "b=2, c=3")

    def test_duplicated_argument(self):
        methods = ["__call__", "normal", "varargs", "varkwargs",
                   "varargskwargs", "static", "klass"]
        self.bad(methods, "1, 2, b=2")

    def test_varargs(self):
        self.good("varargs", "1, 2, 3, 4")
        self.bad("varargs", "1, 2, 3, 4, d=3")

    def test_varkwargs(self):
        self.good("varkwargs", "1, 2, d=3")
        self.bad("varkwargs", "1, 2, 3, 4, d=3")

    def test_varargskwargs(self):
        self.good("varargskwargs", "1, 2, 3, 4, d=3")

    def test_unexistent(self):
        self.bad("unexistent", "")


class ProxyReplacerTest(TestCase):

    def setUp(self):
        self.mocker = CleanMocker()
        import calendar
        self.mock = Mock(self.mocker, object=calendar)
        self.task = ProxyReplacer(self.mock)

    def tearDown(self):
        self.task.restore()

    def test_is_task(self):
        self.assertTrue(isinstance(ProxyReplacer(None), Task))

    def test_mock(self):
        mock = object()
        task = ProxyReplacer(mock)
        self.assertEquals(task.mock, mock)

    def test_defaults_to_not_installed(self):
        import calendar
        self.assertEquals(type(calendar), ModuleType)

    def test_install(self):
        self.task.replay()
        import calendar
        self.assertEquals(type(calendar), Mock)
        self.assertTrue(calendar is self.mock)

    def test_install_protects_mock(self):
        self.task.replay()
        self.assertEquals(type(self.mock.__mocker_object__), ModuleType)

    def test_install_protects_path(self):
        self.task.replay()
        self.assertEquals(type(self.mock.__mocker_path__.root_object),
                          ModuleType)

    def test_deinstall_protects_task(self):
        self.task.replay()
        self.task.restore()
        self.assertEquals(type(self.task.mock), Mock)

    def test_install_protects_anything_with_mocker_replace_false(self):
        class C(object):
            def __init__(self):
                import calendar
                self.calendar = calendar
                self.__mocker_replace__ = False
        obj = C()
        self.task.replay()
        self.assertEquals(type(self.mock.__mocker_path__.root_object),
                          ModuleType)

    def test_install_on_object(self):
        class C(object):
            def __init__(self):
                import calendar
                self.calendar = calendar
        obj = C()
        self.task.replay()
        self.assertEquals(type(obj.calendar), Mock)
        self.assertTrue(obj.calendar is self.mock)

    def test_install_on_submodule(self):
        from os import path
        import os
        mock = Mock(self.mocker, object=path)
        task = ProxyReplacer(mock)
        task.replay()
        try:
            self.assertEquals(type(os.path), Mock)
            self.assertTrue(os.path is mock)
        finally:
            task.restore()

    def test_uninstall_on_restore(self):
        self.task.replay()
        self.task.restore()
        import calendar
        self.assertEquals(type(calendar), ModuleType)
        self.assertEquals(calendar.__name__, "calendar")

    def test_uninstall_from_object(self):
        class C(object):
            def __init__(self):
                import calendar
                self.calendar = calendar
        obj = C()
        self.task.replay()
        self.task.restore()
        self.assertEquals(type(obj.calendar), ModuleType)
        self.assertEquals(obj.calendar.__name__, "calendar")

    def test_uninstall_from_submodule(self):
        from os import path
        import os
        mock = Mock(self.mocker, object=path)
        task = ProxyReplacer(mock)
        self.assertEquals(type(os.path), ModuleType)
        task.replay()
        task.restore()
        self.assertEquals(type(os.path), ModuleType)


class PatcherTest(TestCase):

    def setUp(self):
        self.mocker = Mocker()
        self.patcher = Patcher()
        self.C = type("C", (object,), {})
        self.D = type("D", (self.C,), {})
        self.E = type("E", (), {})

        class MockStub(object):
            def __mocker_act__(self, kind, args=(), kwargs={}, object=None):
                return (kind, args, kwargs, object)

        self.MockStub = MockStub

    def test_is_task(self):
        self.assertTrue(isinstance(Patcher(), Task))

    def test_undefined_repr(self):
        self.assertEquals(repr(Undefined), "Undefined")

    def test_is_monitoring_unseen_class_kind(self):
        self.assertFalse(self.patcher.is_monitoring(self.C, "kind"))

    def test_monitor_class(self):
        self.patcher.monitor(self.C, "kind")
        self.assertTrue(self.patcher.is_monitoring(self.C, "kind"))

    def test_monitor_subclass(self):
        self.patcher.monitor(self.C, "kind")
        self.assertTrue(self.patcher.is_monitoring(self.D, "kind"))

    def test_monitor_unknown_class(self):
        self.patcher.monitor(self.C, "kind")
        self.assertFalse(self.patcher.is_monitoring(self.E, "kind"))

    def test_is_monitoring_unseen_instance(self):
        obj = self.E()
        self.patcher.monitor(self.C, "kind")
        self.assertFalse(self.patcher.is_monitoring(obj, "kind"))

    def test_is_monitoring_instance_explicitly_monitored(self):
        obj = self.C()
        self.patcher.monitor(obj, "kind")
        self.assertTrue(self.patcher.is_monitoring(obj, "kind"))

    def test_is_monitoring_instance_monitored_by_class(self):
        obj = self.D()
        self.patcher.monitor(self.D, "kind")
        self.assertTrue(self.patcher.is_monitoring(obj, "kind"))

    def test_patch_attr(self):
        self.patcher.patch_attr(self.C, "attr", "patch")
        self.assertEquals(self.C.__dict__.get("attr"), "patch")

    def test_patch_attr_and_restore(self):
        self.patcher.patch_attr(self.C, "attr", "patch")
        self.patcher.restore()
        self.assertTrue("attr" not in self.C.__dict__)

    def test_patch_attr_and_restore_to_original(self):
        self.C.attr = "original"
        self.patcher.patch_attr(self.C, "attr", "patch")
        self.patcher.restore()
        self.assertEquals(self.C.__dict__.get("attr"), "original")

    def test_get_unpatched_attr_unpatched_undefined(self):
        self.assertEquals(self.patcher.get_unpatched_attr(self.C, "attr"),
                          Undefined)

    def test_get_unpatched_attr_unpatched(self):
        self.C.attr = "original"
        self.assertEquals(self.patcher.get_unpatched_attr(self.C, "attr"),
                          "original")

    def test_get_unpatched_attr_defined_on_superclass(self):
        self.C.attr = "original"
        self.assertEquals(self.patcher.get_unpatched_attr(self.D, "attr"),
                          "original")

    def test_get_unpatched_attr_defined_on_superclass_patched_on_sub(self):
        self.C.attr = "original"
        self.patcher.patch_attr(self.D, "attr", "patch")
        self.assertEquals(self.patcher.get_unpatched_attr(self.D, "attr"),
                          "original")

    def test_get_unpatched_attr_patched_originally_undefined(self):
        self.patcher.patch_attr(self.C, "attr", "patch")
        self.assertEquals(self.patcher.get_unpatched_attr(self.C, "attr"),
                          Undefined)

    def test_get_unpatched_attr_patched(self):
        self.C.attr = "original"
        self.patcher.patch_attr(self.C, "attr", "patch")
        self.assertEquals(self.patcher.get_unpatched_attr(self.C, "attr"),
                          "original")

    def test_get_unpatched_attr_on_instance_originally_undefined(self):
        self.assertEquals(self.patcher.get_unpatched_attr(self.C(), "attr"),
                          Undefined)

    def test_get_unpatched_attr_on_instance(self):
        self.C.attr = "original"
        self.assertEquals(self.patcher.get_unpatched_attr(self.D(), "attr"),
                          "original")

    def test_get_unpatched_attr_on_instance_defined_on_superclass(self):
        self.C.attr = "original"
        self.patcher.patch_attr(self.C, "attr", "patch")
        self.assertEquals(self.patcher.get_unpatched_attr(self.D(), "attr"),
                          "original")

    def test_get_unpatched_attr_on_instance_with_descriptor(self):
        self.C.attr = property(lambda self: "original")
        self.patcher.patch_attr(self.C, "attr", "patch")
        self.assertEquals(self.patcher.get_unpatched_attr(self.D(), "attr"),
                          "original")

    def test_get_unpatched_attr_on_subclass_with_descriptor(self):
        calls = []
        class Property(object):
            def __get__(self, obj, cls):
                calls.append((obj, cls))
                return "original"
        self.C.attr = Property()
        self.patcher.patch_attr(self.C, "attr", "patch")
        self.assertEquals(self.patcher.get_unpatched_attr(self.D, "attr"),
                          "original")
        self.assertEquals(calls, [(None, self.D)])

    def test_get_unpatched_attr_on_instance_with_fake_descriptor(self):
        class BadProperty(object):
            def __init__(self):
                # On real, __get__ must be on the class, not on the instance.
                self.__get__ = lambda self, obj, cls=None: "original"
        prop = BadProperty()
        self.C.attr = prop
        self.patcher.patch_attr(self.C, "attr", "patch")
        self.assertEquals(self.patcher.get_unpatched_attr(self.D(), "attr"),
                          prop)

    def test_replay_with_monitored_class(self):
        self.patcher.monitor(self.C, "call")
        self.patcher.replay()
        self.assertEquals(type(self.C.__dict__["__call__"]), PatchedMethod)

    def test_replay_with_monitored_instance(self):
        self.patcher.monitor(self.C(), "call")
        self.patcher.replay()
        self.assertEquals(type(self.C.__dict__["__call__"]), PatchedMethod)

    def test_replay_getattr(self):
        self.patcher.monitor(self.C, "getattr")
        self.patcher.replay()
        self.assertEquals(type(self.C.__dict__["__getattribute__"]),
                          PatchedMethod)

    def test_restore(self):
        self.patcher.monitor(self.C, "call")
        self.patcher.replay()
        self.patcher.restore()
        self.assertTrue("__call__" not in self.C.__dict__)

    def test_restore_twice_does_nothing(self):
        self.patcher.monitor(self.C, "call")
        self.patcher.replay()
        self.patcher.restore()
        self.C.__call__ = "original"
        self.patcher.restore()
        self.assertTrue(self.C.__dict__.get("__call__"), "original")

    def test_patched_call_on_instance(self):
        self.patcher.monitor(self.C, "call")
        obj = self.C()
        obj.__mocker_mock__ = self.MockStub()
        self.patcher.replay()
        result = obj(1, a=2)
        self.assertEquals(result, ("call", (1,), {"a": 2}, obj))

    def test_patched_call_on_class(self):
        self.patcher.monitor(self.C, "call")
        self.C.__mocker_mock__ = self.MockStub()
        self.patcher.replay()
        obj = self.C()
        result = obj(1, a=2)
        self.assertEquals(result, ("call", (1,), {"a": 2}, obj))

    def test_patched_call_on_class_edge_case(self):
        """Only "getattr" kind should passthrough on __mocker_* arguments."""
        self.patcher.monitor(self.C, "call")
        self.C.__mocker_mock__ = self.MockStub()
        self.patcher.replay()
        obj = self.C()
        result = obj("__mocker_mock__")
        self.assertEquals(result, ("call", ("__mocker_mock__",), {}, obj))

    def test_patched_getattr_on_class(self):
        self.patcher.monitor(self.C, "getattr")
        self.C.__mocker_mock__ = self.MockStub()
        self.patcher.replay()
        obj = self.C()
        result = obj.attr
        self.assertEquals(result, ("getattr", ("attr",), {}, obj))

    def test_patched_getattr_on_unmonitored_object(self):
        obj1 = self.C()
        obj1.__mocker_mock__ = self.MockStub()
        self.patcher.monitor(obj1, "getattr")
        obj2 = self.C()
        obj2.attr = "original"
        self.patcher.replay()
        self.assertEquals(obj1.attr, ("getattr", ("attr",), {}, obj1))
        self.assertEquals(obj2.attr, "original")

    def test_patched_getattr_on_different_instances(self):
        def build_getattr(original):
            def __getattribute__(self, name):
                if name == "attr":
                    return original
                return object.__getattribute__(self, name)
            return __getattribute__
        self.C.__getattribute__ = build_getattr("originalC")
        self.D.__getattribute__ = build_getattr("originalD")

        class MockStub(object):
            def __init__(self, id):
                self.id = id
            def __mocker_act__(self, kind, args=(), kwargs={}, object=None):
                return self.id

        obj1, obj2, obj3, obj4, obj5, obj6 = [self.C() for i in range(6)]
        obj7, obj8, obj9 = [self.D() for i in range(3)]

        obj2.__mocker_mock__ = MockStub(2)
        self.patcher.monitor(obj2, "getattr")
        obj5.__mocker_mock__ = MockStub(5)
        self.patcher.monitor(obj5, "getattr")
        obj8.__mocker_mock__ = MockStub(8)
        self.patcher.monitor(obj8, "getattr")

        self.patcher.replay()
        self.assertEquals(obj1.attr, "originalC")
        self.assertEquals(obj2.attr, 2)
        self.assertEquals(obj3.attr, "originalC")
        self.assertEquals(obj4.attr, "originalC")
        self.assertEquals(obj5.attr, 5)
        self.assertEquals(obj6.attr, "originalC")
        self.assertEquals(obj7.attr, "originalD")
        self.assertEquals(obj8.attr, 8)
        self.assertEquals(obj9.attr, "originalD")

    def test_patched_getattr_execute_getattr(self):
        class C(object):
            def __getattribute__(self, attr):
                if attr == "attr":
                    return "original"
        action = Action("getattr", ("attr",), {})
        obj = C()
        self.patcher.monitor(obj, "getattr")
        self.patcher.replay()
        self.assertEquals(self.patcher.execute(action, obj), "original")

    def test_execute_getattr_on_unexistent(self):
        action = Action("getattr", ("attr",), {})
        obj = self.C()
        self.patcher.monitor(obj, "getattr")
        self.patcher.replay()
        self.assertRaises(AttributeError, self.patcher.execute, action, obj)

    def test_patched_real_getattr_on_different_instances(self):
        def build_getattr(original):
            def __getattr__(self, name):
                if name == "attr":
                    return original
                return object.__getattr__(self, name)
            return __getattr__
        self.C.__getattr__ = build_getattr("originalC")
        self.D.__getattr__ = build_getattr("originalD")

        class MockStub(object):
            def __init__(self, id):
                self.id = id
            def __mocker_act__(self, kind, args=(), kwargs={}, object=None):
                return self.id

        obj1, obj2, obj3, obj4, obj5, obj6 = [self.C() for i in range(6)]
        obj7, obj8, obj9 = [self.D() for i in range(3)]

        obj2.__mocker_mock__ = MockStub(2)
        self.patcher.monitor(obj2, "getattr")
        obj5.__mocker_mock__ = MockStub(5)
        self.patcher.monitor(obj5, "getattr")
        obj8.__mocker_mock__ = MockStub(8)
        self.patcher.monitor(obj8, "getattr")

        self.patcher.replay()
        self.assertEquals(obj1.attr, "originalC")
        self.assertEquals(obj2.attr, 2)
        self.assertEquals(obj3.attr, "originalC")
        self.assertEquals(obj4.attr, "originalC")
        self.assertEquals(obj5.attr, 5)
        self.assertEquals(obj6.attr, "originalC")
        self.assertEquals(obj7.attr, "originalD")
        self.assertEquals(obj8.attr, 8)
        self.assertEquals(obj9.attr, "originalD")

    def test_patched_real_getattr_execute_getattr(self):
        class C(object):
            def __getattr__(self, attr):
                if attr == "attr":
                    return "original"
        action = Action("getattr", ("attr",), {})
        obj = C()
        self.patcher.monitor(obj, "getattr")
        self.patcher.replay()
        self.assertEquals(self.patcher.execute(action, obj), "original")

    def test_execute_call(self):
        class C(object):
            def __call__(self, *args, **kwargs):
                return (args, kwargs)
        action = Action("call", (1,), {"a": 2})
        obj = C()
        self.patcher.monitor(obj, "call")
        self.patcher.replay()
        self.assertEquals(self.patcher.execute(action, obj), ((1,), {"a": 2}))

    def test_recorder_class_getattr(self):
        self.C.method = lambda: None
        mock = self.mocker.patch(self.C)
        mock.method()
        self.mocker.result("mocked")
        self.mocker.replay()
        self.assertEquals(self.C().method(), "mocked")
        self.assertRaises(AssertionError, self.C().method)

    def test_recorder_instance_getattr(self):
        self.C.attr = "original"
        obj1 = self.C()
        obj2 = self.C()
        mock = self.mocker.patch(obj1)
        mock.attr
        self.mocker.result("mocked")
        self.mocker.replay()
        self.assertEquals(obj1.attr, "mocked")
        self.assertRaises(AssertionError, getattr, obj1, "attr")
        self.assertEquals(obj2.attr, "original")
        self.assertRaises(AttributeError, getattr, obj1, "unexistent")

    def test_recorder_passthrough(self):
        class C(object):
            def __init__(self):
                self.result = "original" # Value on object's dictionary.
            def method(self):
                return self.result
        mock = self.mocker.patch(C)
        mock.method()
        self.mocker.passthrough()
        self.mocker.replay()
        obj = C()
        self.assertEquals(obj.method(), "original")
        self.assertRaises(AssertionError, obj.method)

    def test_original_exception_raised(self):
        class C(object):
            def use_non_existing_attribute(self):
                return self.bad_attribute

        mock = self.mocker.patch(C)
        mock.any_other_method()
        self.mocker.replay()
        obj = C()
        try:
            obj.use_non_existing_attribute()
        except AttributeError, error:
            message = "'C' object has no attribute 'bad_attribute'"
            self.assertEquals(message, str(error))


def main():
    try:
        unittest.main()
    finally:
        print
        if coverage:
            coverage.stop()
            (filename, executed,
             missing, missing_human) = coverage.analysis("mocker.py")
            if missing:
                print "WARNING: Some statements were not executed:"
                print
                coverage.report(["mocker.py"])
            else:
                print "Tests covered 100% of statements!"
        else:
            print "WARNING: No coverage test performed (missing coverage.py)"
        print


if __name__ == "__main__":
    main()
