sinojelly / mockcpp

Two C/C++ testing tools, mockcpp and testngpp.
Apache License 2.0
66 stars 39 forks source link

testShouldBeAbleToSpecifyAnObjectShouldKeepAlive failed due to unexpected crash #16

Closed earthelf closed 3 years ago

earthelf commented 3 years ago

env

gcc 7.3 + linux

failed testcases

-------------------SUITE: TestMockObject2-------------------

(TestMockObject2)
..............terminate called after throwing an instance of 'mockcpp::Exception'
  what():  trying to delete an object expected keeping alive.

[ CRASHED  ] testShouldBeAbleToSpecifyAnObjectShouldKeepAlive - TestMockObject2.h:246: test crashed unexpectedly.
...

------------------SUITE: TestVirtualTable-------------------

(TestVirtualTable)
..........terminate called after throwing an instance of 'mockcpp::Exception'
  what():  trying to delete an object expected keeping alive.

[ CRASHED  ] shouldFailThrowExceptionWhileTryingToDeleteThePointerWhichWasExpectedNotTo - TestVirtualTable.h:262: test crashed unexpectedly.
....

对应关键代码

247        {
248     // TODO: Temporary disable this case on MinGW : mockcpp::Exception:  what():  trying to delete an object expected keeping alive.
249     #if !defined(__MINGW32__)
250           MockObject<Interface> mock;
251           mock.willKeepAlive();
252           TS_ASSERT_THROWS(delete (Interface*) mock, Exception);
253     #endif
254        }
255

堆栈

(gdb) bt
#0  0x00007fbaddcca4e7 in raise () from /lib64/libc.so.6
#1  0x00007fbaddccbbd8 in abort () from /lib64/libc.so.6
#2  0x00007fbade60fd95 in __gnu_cxx::__verbose_terminate_handler() () from /lib64/libstdc++.so.6
#3  0x00007fbade60db66 in ?? () from /lib64/libstdc++.so.6
#4  0x00007fbade60cb29 in ?? () from /lib64/libstdc++.so.6
#5  0x00007fbade60d498 in __gxx_personality_v0 () from /lib64/libstdc++.so.6
#6  0x00007fbade074913 in ?? () from /lib64/libgcc_s.so.1
#7  0x00007fbade074e57 in _Unwind_Resume () from /lib64/libgcc_s.so.1
#8  0x00007fbadda2762b in mockcpp::(anonymous namespace)::DestructorHolder<0u, mockcpp::(anonymous namespace)::DummyClass>::destructor(void*) () from libmockcpp-ut-TestMockObject2.so
#9  0x00007fbadd99c6c6 in TestMockObject2::testShouldBeAbleToSpecifyAnObjectShouldKeepAlive (this=0x2083fe0) at /opt/build/mockcpp/tests/ut/TestMockObject2.h:252
#10 0x00007fbadd99fa37 in TESTCASE_TestMockObject2_testShouldBeAbleToSpecifyAnObjectShouldKeepAlive::runTest (this=0x7fbaddc92e80 <testcase_instance_TestMockObject2_testShouldBeAbleToSpecifyAnObjectShouldKeepAlive>)
    at /opt/build/mockcpp/tests/ut/TestMockObject2.cpp:875
#11 0x00000000004164a9 in testngpp::TestCase::run (this=0x7fbaddc92e80 <testcase_instance_TestMockObject2_testShouldBeAbleToSpecifyAnObjectShouldKeepAlive>)
    at /opt/build/mockcpp/tests/3rdparty/testngpp/include/testngpp/internal/TestCase.h:88

原因分析

destructor 填充到虚表作为析构函数, 编译器调用析构函数,是约定析构函数不能外抛异常的,所以编译器直接省略了外层的 catch 语句,直接就走到了 std::terminate 上

建议方式

可以考虑直接测试 destructor 函数, 或者把 delete (Interface*) mock 语句封装成一个函数, 然后再断言, 即可正常工作

diff --git a/tests/ut/TestMockObject2.h b/tests/ut/TestMockObject2.h
index 5273efa..ec0ffd9 100644
--- a/tests/ut/TestMockObject2.h
+++ b/tests/ut/TestMockObject2.h
@@ -242,6 +242,11 @@ public:
       TS_ASSERT_THROWS_NOTHING(mock.verify());
    }

+   void cleanMock(MockObject<Interface>& mock)
+   {
+       delete (Interface*) mock;
+   }
+
    // willKeepAlive
    void testShouldBeAbleToSpecifyAnObjectShouldKeepAlive()
    {
@@ -249,7 +254,8 @@ public:
 #if !defined(__MINGW32__)
       MockObject<Interface> mock;
       mock.willKeepAlive();
-      TS_ASSERT_THROWS(delete (Interface*) mock, Exception);
+      // use wrapped call to prevent gcc skip catch action
+      TS_ASSERT_THROWS(cleanMock(mock), Exception);
 #endif
    }
sinojelly commented 3 years ago

谢谢,挺详细的。是不是两个testcase的问题都可以类似的解决? 帮忙提个 pull request 如何?

earthelf commented 3 years ago

谢谢,挺详细的。是不是两个testcase的问题都可以类似的解决? 帮忙提个 pull request 如何?

两个 case 是一样的原因,可以一样的修改,

目前在公司不方便提交代码, 可能过段时间才有可以在家开发的电脑 :D

而且我刚学习 mockcpp, 也不清楚最佳修改方案