#include #include "cotest/cotest.h" #include "gtest/gtest-spi.h" using namespace std; using ::testing::StrictMock; using namespace testing; ////////////////////////////////////////////// // Mocking assets class ClassToMock { public: virtual ~ClassToMock() {} virtual int Mock1(int i) const = 0; virtual int Mock2(int i, int j) const = 0; virtual int Mock3(int i) const = 0; virtual int Mock4(int i) const = 0; virtual int Mock4(int i) = 0; virtual void Mock5(int i) const = 0; virtual void Mock6(int i, int j) const = 0; }; class MockClass : public ClassToMock { public: MOCK_METHOD(int, Mock1, (int i), (const, override)); MOCK_METHOD(int, Mock2, (int i, int j), (const, override)); MOCK_METHOD(int, Mock3, (int i), (const, override)); MOCK_METHOD(int, Mock4, (int i), (const, override)); MOCK_METHOD(int, Mock4, (int i), (override)); MOCK_METHOD(void, Mock5, (int i), (const, override)); MOCK_METHOD(void, Mock6, (int i, int j), (const, override)); }; using ::testing::Return; ////////////////////////////////////////////// // The actual tests TEST(ExteriorWildcardTest, TwoMethodWaiting) { StrictMock mock_object; StrictMock mock_object2; // Try doing this early, to simulate a generic setup phase EXPECT_CALL(mock_object2, Mock1).WillRepeatedly(Return(-2)); auto coro = COROUTINE(TwoMethodWaiting) { auto cg = WAIT_FOR_CALL(mock_object); EXPECT_TRUE(cg.IS_CALL(mock_object, Mock1(200)).RETURN(20)); auto cg2 = WAIT_FOR_CALL(mock_object); EXPECT_TRUE(cg2.IS_CALL(mock_object, Mock2(200, 400)).RETURN(30)); }; EXPECT_CALL(mock_object2, Mock2).Times(2).WillRepeatedly(Return(-3)); coro.WATCH_CALL(); EXPECT_EQ(mock_object2.Mock1(500), -2); EXPECT_EQ(mock_object2.Mock2(500, 600), -3); EXPECT_EQ(mock_object.Mock1(200), 20); EXPECT_EQ(mock_object2.Mock1(501), -2); EXPECT_EQ(mock_object2.Mock2(501, 601), -3); EXPECT_EQ(mock_object.Mock2(200, 400), 30); } TEST(ExteriorWildcardTest, TwoMethodWaitingMO) { StrictMock mock_object; StrictMock mock_object2; auto coro = COROUTINE(TwoMethodWaiting) { auto cg = WAIT_FOR_CALL(); EXPECT_TRUE(cg.IS_CALL(mock_object, Mock1(200)).RETURN(20)); auto cg2 = WAIT_FOR_CALL(); EXPECT_TRUE(cg2.IS_CALL(mock_object, Mock2(200, 400)).RETURN(30)); }; EXPECT_CALL(mock_object2, Mock1).Times(2).WillRepeatedly(Return(-2)); EXPECT_CALL(mock_object2, Mock2).Times(2).WillRepeatedly(Return(-3)); coro.WATCH_CALL(mock_object); EXPECT_EQ(mock_object2.Mock1(500), -2); EXPECT_EQ(mock_object2.Mock2(500, 600), -3); EXPECT_EQ(mock_object.Mock1(200), 20); EXPECT_EQ(mock_object2.Mock1(501), -2); EXPECT_EQ(mock_object2.Mock2(501, 601), -3); EXPECT_EQ(mock_object.Mock2(200, 400), 30); } TEST(ExteriorWildcardTest, TwoMethodWaitingPre) { StrictMock mock_object; StrictMock mock_object2; auto coro = COROUTINE(TwoMethodWaitingPre) { auto cg = WAIT_FOR_CALL(mock_object); EXPECT_TRUE(cg.IS_CALL(mock_object, Mock1(200)).RETURN(20)); cg = WAIT_FOR_CALL(mock_object); EXPECT_TRUE(cg.IS_CALL(mock_object, Mock2(200, 400)).RETURN(30)); }; EXPECT_CALL(mock_object2, Mock1).Times(2).WillRepeatedly(Return(-2)); EXPECT_CALL(mock_object2, Mock2).Times(2).WillRepeatedly(Return(-3)); coro.WATCH_CALL(); EXPECT_CALL(mock_object, Mock1(1000)).WillRepeatedly(Return(-10)); EXPECT_CALL(mock_object2, Mock1(1100)).WillRepeatedly(Return(-11)); EXPECT_EQ(mock_object2.Mock1(500), -2); EXPECT_EQ(mock_object2.Mock2(500, 600), -3); EXPECT_EQ(mock_object.Mock1(200), 20); EXPECT_EQ(mock_object.Mock1(1000), -10); EXPECT_EQ(mock_object2.Mock1(1100), -11); EXPECT_EQ(mock_object2.Mock1(501), -2); EXPECT_EQ(mock_object2.Mock2(501, 601), -3); EXPECT_EQ(mock_object.Mock2(200, 400), 30); } TEST(ExteriorWildcardTest, TwoMethodWaitingPreMO) { StrictMock mock_object; StrictMock mock_object2; auto coro = COROUTINE(TwoMethodWaitingPre) { auto cg = WAIT_FOR_CALL(); EXPECT_TRUE(cg.IS_CALL(mock_object, Mock1(200)).RETURN(20)); cg = WAIT_FOR_CALL(); EXPECT_TRUE(cg.IS_CALL(mock_object, Mock2(200, 400)).RETURN(30)); }; EXPECT_CALL(mock_object2, Mock1).Times(2).WillRepeatedly(Return(-2)); EXPECT_CALL(mock_object2, Mock2).Times(2).WillRepeatedly(Return(-3)); coro.WATCH_CALL(mock_object); EXPECT_CALL(mock_object, Mock1(1000)).WillRepeatedly(Return(-10)); EXPECT_CALL(mock_object2, Mock1(1100)).WillRepeatedly(Return(-11)); EXPECT_EQ(mock_object2.Mock1(500), -2); EXPECT_EQ(mock_object2.Mock2(500, 600), -3); EXPECT_EQ(mock_object.Mock1(200), 20); EXPECT_EQ(mock_object.Mock1(1000), -10); EXPECT_EQ(mock_object2.Mock1(1100), -11); EXPECT_EQ(mock_object2.Mock1(501), -2); EXPECT_EQ(mock_object2.Mock2(501, 601), -3); EXPECT_EQ(mock_object.Mock2(200, 400), 30); } TEST(ExteriorWildcardTest, MultiPriority) { StrictMock mock_object; StrictMock mock_object2; auto coro1 = COROUTINE() { auto cg = WAIT_FOR_CALL(mock_object); EXPECT_TRUE(cg.IS_CALL(mock_object, Mock1(200)).RETURN(20)); // Exit without RETIRE() is saturation }; auto coro2 = COROUTINE() { auto cg = WAIT_FOR_CALL(mock_object2); EXPECT_TRUE(cg.IS_CALL(mock_object2, Mock2(_, 400)).With(Lt()).RETURN(30)); RETIRE(); }; coro1.WATCH_CALL(); EXPECT_CALL(mock_object, Mock1).WillOnce(Return(-10)).RetiresOnSaturation(); // #1 EXPECT_CALL(mock_object2, Mock2).WillOnce(Return(-11)); // #2 coro2.WATCH_CALL(); EXPECT_EQ(mock_object.Mock1(1000), -10); // coro2's wait drops; expectation #1 matches and retires EXPECT_EQ(mock_object.Mock1(200), 20); // coro2's wait drops; expectation #1 has retired; coro1 // accepts and is saturated EXPECT_EQ(mock_object2.Mock2(200, 400), 30); // coro2 accepts and retires EXPECT_EQ(mock_object2.Mock2(200, 400), -11); // coro2 has retired; expectation #2 matches and is saturated } TEST(ExteriorWildcardTest, MultiPriorityMO) { StrictMock mock_object; StrictMock mock_object2; auto coro1 = COROUTINE() { auto cg = WAIT_FOR_CALL(); EXPECT_TRUE(cg.IS_CALL(mock_object, Mock1(200)).RETURN(20)); // Exit without RETIRE() is saturation }; auto coro2 = COROUTINE() { auto cg = WAIT_FOR_CALL(); EXPECT_TRUE(cg.IS_CALL(mock_object2, Mock2(_, 400)).With(Lt()).RETURN(30)); RETIRE(); }; coro1.WATCH_CALL(mock_object); EXPECT_CALL(mock_object, Mock1).WillOnce(Return(-10)).RetiresOnSaturation(); // #1 EXPECT_CALL(mock_object2, Mock2).WillOnce(Return(-11)); // #2 coro2.WATCH_CALL(mock_object2); EXPECT_EQ(mock_object.Mock1(1000), -10); // coro2's wait drops; expectation #1 matches and retires EXPECT_EQ(mock_object.Mock1(200), 20); // coro2's wait drops; expectation #1 has retired; coro1 // accepts and is saturated EXPECT_EQ(mock_object2.Mock2(200, 400), 30); // coro2 accepts and retires EXPECT_EQ(mock_object2.Mock2(200, 400), -11); // coro2 has retired; expectation #2 matches and is saturated } TEST(ExteriorWildcardTest, MockObjectAddressAlias) { auto coro = COROUTINE() { WAIT_FOR_CALL().RETURN(); WAIT_FOR_CALL().RETURN(); }; { StrictMock mock_object; coro.WATCH_CALL(mock_object); mock_object.Mock5(200); } { StrictMock mock_object2; // Known bug with WATCH_CALL( mock object ) // This call should not make it into the coroutine // but it does because mock_object2 is at the same address as // the now-deleted mock_object1. Not easy to fix: consider if // we hadn't made any calls on mock_object - then the GMock code // that registers the mocker would not have run. If we assume // a call, then we could maybe use GMock's registry to recover // the relationship - then we'd also need DetachMocker() so that // the CotestWatcher can deduce that it needs to detach from the // mock object too. // However, the test case to cause this is very strange and seems // to require the coro to be outside the scope of the mock objects - // otherwise Watchers will be discarded before here. mock_object2.Mock6(200, 400); } } TEST(ExteriorWildcardTest, StackedCoros) { StrictMock mock_object; StrictMock mock_object2; auto coro1 = COROUTINE(coro1) { WATCH_CALL(); // watch all mock calls auto cg = WAIT_FOR_CALL(); EXPECT_TRUE(cg.IS_CALL(mock_object, Mock1(1000)).RETURN(-10)); cg = WAIT_FOR_CALL(mock_object); EXPECT_TRUE(cg.IS_CALL(mock_object, Mock1(200)).RETURN(20)); auto cs = WAIT_FOR_CALL(mock_object, Mock2); EXPECT_TRUE(cs.IS_CALL(mock_object, Mock2(220, _))); cs.RETURN(-11); RETIRE(); }; auto coro2 = COROUTINE(coro2) { WATCH_CALL(); // watch all mock calls auto cg = WAIT_FOR_CALL(mock_object2); EXPECT_TRUE(cg.IS_CALL(mock_object2, Mock2(_, 400)).With(Lt()).RETURN(30)); auto cs = WAIT_FOR_CALL(mock_object, Mock1(1100)); cs.RETURN(-5); cg = WAIT_FOR_CALL(); EXPECT_TRUE(cg.IS_CALL(mock_object2, Mock2(300, 350)).With(Lt()).RETURN(33)); SATISFY(); cg = WAIT_FOR_CALL(mock_object).RETURN(); }; EXPECT_EQ(mock_object.Mock1(1000), -10); // c2 is looking for any mock call on mo2, so drops it and c1 // is looking for any so accepts, checks, returns EXPECT_EQ(mock_object2.Mock2(200, 400), 30); // c2 is looking for any mock call on mo2, so accepts, checks, // returns EXPECT_EQ(mock_object.Mock1(200), 20); // c2 is now looking for mo.MM1 with arg==1100, so drops it and c1 // is now looking for any call on mo so accepts, checks, returns EXPECT_EQ(mock_object.Mock2(220, 400), -11); // c2 is still looking for mo.MM1 with arg==1100, so drops it and // c1 is now looking for mo.MM2 mo so accepts, checks, returns EXPECT_EQ(mock_object.Mock1(1100), -5); // c2 is still looking for mo.MM1 with arg==1100, so accepts, // checks, returns EXPECT_EQ(mock_object2.Mock2(300, 350), 33); // c2 is now looking for any call so accepts, checks, returns // c1 has retired, which means it will drop any further calls (none are made // here) c2 is left looking for any call on mo, but it's satisfied, so if the // call never arrives, there's no error }