Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't catch exception: terminate called after throwing an instance of 'trompeloeil::expectation_violation' #216

Open
liarokapisv opened this issue Nov 8, 2020 · 1 comment

Comments

@liarokapisv
Copy link

liarokapisv commented Nov 8, 2020

Hi there @rollbear thanks for your incredible library!

I have the following issue, it appears that for some reason the following snippet of code results in a terminate call.

terminate called after throwing an instance of 'trompeloeil::expectation_violation'
  what():  /home/veritas/projects/effect_pedal/tests/src/delay_controller.cpp:432
Unfulfilled expectation:
Expected sut.test() to be called once, actually never called

Code:

struct test_sut
{
    MAKE_MOCK0(test, void());
};

int main()
{
    try {
        test_sut sut;
        REQUIRE_CALL(sut, test());
    } 
    catch (...) 
    {
        std::cout << "caught" << std::endl;
    }

    return 0;
}

Preprocessed:

struct test_sut
{
    private: 
        using trompeloeil_l_cardinality_match_425 = std::integral_constant<bool, 0 == ::trompeloeil::param_list< void()>::size>; 
        static_assert(trompeloeil_l_cardinality_match_425::value, "Function signature does not have " "0" " parameters"); 
        using trompeloeil_l_matcher_list_t_425 = ::trompeloeil::call_matcher_list< void()>; 
        using trompeloeil_l_expectation_list_t_425 = ::trompeloeil::expectations<trompeloeil_movable_mock, void()>; 
        struct trompeloeil_l_tag_type_trompeloeil_425 
        { 
            const char* trompeloeil_expectation_file; 
            unsigned long trompeloeil_expectation_line; 
            const char *trompeloeil_expectation_string; 
            using trompeloeil_sig_t = typename ::trompeloeil::identity_type< void()>::type; 
            using trompeloeil_call_params_type_t = ::trompeloeil::call_params_type_t< void()>; 
            using trompeloeil_return_of_t = ::trompeloeil::return_of_t< void()>; 
            template <typename ... trompeloeil_param_type> 
            auto test( trompeloeil_param_type&& ... trompeloeil_param) 
                -> ::trompeloeil::modifier_t<
                        trompeloeil_sig_t, 
                        trompeloeil_l_tag_type_trompeloeil_425, 
                        trompeloeil_param_type...> 
            { 
                using matcher = ::trompeloeil::call_matcher< void(), ::trompeloeil::param_t<trompeloeil_param_type...>>; 
                return { 
                    new matcher { 
                        trompeloeil_expectation_file, 
                        trompeloeil_expectation_line, 
                        trompeloeil_expectation_string, 
                        std::forward<trompeloeil_param_type>(trompeloeil_param)... 
                    } }; 
            }
        }; 
    public: 
        trompeloeil_l_matcher_list_t_425& trompeloeil_matcher_list(trompeloeil_l_tag_type_trompeloeil_425*) noexcept 
        { 
            return trompeloeil_l_expectations_425.active; 
        } 

        ::trompeloeil::return_of_t< void()> test() 
        { 
            return ::trompeloeil::mock_func<trompeloeil_movable_mock, void()>(
                        trompeloeil_l_cardinality_match_425{}, 
                        trompeloeil_l_expectations_425, 
                        "test", 
                        "void()" ); 
        } 

        auto trompeloeil_self_test() -> decltype(*this); 
        trompeloeil_l_tag_type_trompeloeil_425 trompeloeil_tag_test(); 

    private: 
        mutable trompeloeil_l_expectation_list_t_425 trompeloeil_l_expectations_425{}; 

    public: 
        using trompeloeil_l_unused_alias_425 = void;
};

int main()
{
    try {
        test_sut sut;
        auto trompeloeil_c_call_obj_7 = 
            ::trompeloeil::call_validator_t<
                decltype((sut).trompeloeil_self_test())>{(sut)} + 
                ::trompeloeil::detail::conditional_t<false, 
                    decltype((sut).test()), 
                    decltype((sut).trompeloeil_tag_test())> {"path_to_file", static_cast<unsigned long>(432), "sut" "." "test()"}.test();
    }
    catch (...)
    {
        std::cout << "caught" << std::endl;
    }

    return 0;
}

For some reason the exception is not catch-able, I assume something is thrown from a noexcept context. This turns out to be very problematic when integrating with rapidcheck which depends on being able to catch exceptions in order to be able to report the failing command sequence.

Backtrace:

#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff74838b1 in __GI_abort () at abort.c:79
#2  0x00007ffff7ad8957 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7adeae6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff7addb49 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff7ade4b8 in __gxx_personality_v0 () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x00007ffff7844573 in ?? () from /lib/x86_64-linux-gnu/libgcc_s.so.1
#7  0x00007ffff7844df5 in _Unwind_Resume () from /lib/x86_64-linux-gnu/libgcc_s.so.1
#8  0x000055555555a1e2 in trompeloeil::report_unfulfilled (reason=0x55555557185b "Unfulfilled expectation", name=0x555555571602 "sut.test()", values="", min_calls=1, call_count=0, loc=...)
    at /home/veritas/.conan/data/trompeloeil/v39/rollbear/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/trompeloeil.hpp:3846
#9  0x0000555555560abb in trompeloeil::call_matcher<void (), std::tuple<> >::report_missed(char const*) (this=0x555555794e70, reason=0x55555557185b "Unfulfilled expectation")
    at /home/veritas/.conan/data/trompeloeil/v39/rollbear/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/trompeloeil.hpp:4091
#10 0x000055555555f72d in trompeloeil::call_matcher<void (), std::tuple<> >::~call_matcher() (this=0x555555794e70, __in_chrg=<optimized out>)
    at /home/veritas/.conan/data/trompeloeil/v39/rollbear/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/trompeloeil.hpp:3916
#11 0x000055555555f7da in trompeloeil::call_matcher<void (), std::tuple<> >::~call_matcher() (this=0x555555794e70, __in_chrg=<optimized out>)
    at /home/veritas/.conan/data/trompeloeil/v39/rollbear/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/trompeloeil.hpp:3919
#12 0x000055555555cac8 in std::default_delete<trompeloeil::expectation>::operator() (this=0x7fffffffdb78, __ptr=0x555555794ea0) at /usr/include/c++/7/bits/unique_ptr.h:78
#13 0x000055555555b927 in std::unique_ptr<trompeloeil::expectation, std::default_delete<trompeloeil::expectation> >::~unique_ptr (this=0x7fffffffdb78, __in_chrg=<optimized out>)
    at /usr/include/c++/7/bits/unique_ptr.h:263
#14 0x0000555555557fea in main () at /home/veritas/projects/effect_pedal/tests/src/delay_controller.cpp:432
@rollbear
Copy link
Owner

Yes, I know.

If you haven't provided an adapter for reporting errors, all errors are reported by throwing an exception. I don't know of any other reasonable default, but it has its bad effects. If the error is detected and reported in a destructor called during stack rollback due to an exception, you get the infamous throw while throwing situation which is mandated by the standard to call std::terminate(). See http://eel.is/c++draft/except.terminate#1.4

The simple solution is to write an adapter. There are several already available for many popular unit testing frameworks, and if you need another it's not difficult to write one (please submit your adapter for inclusion in the source tree for future users.) See https://github.com/rollbear/trompeloeil/blob/master/docs/CookBook.md#unit_test_frameworks. The hard core solution is to change the C++ standard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants