Skip to content

Conversation

@rgolovanov
Copy link

@rgolovanov rgolovanov commented May 18, 2017

fix of #1097
There is a problem happened when GMock is used with 3rdParty UT-engine based on exceptions handling.

If I enables throwing exceptions from GMock: ::testing::GTEST_FLAG(throw_on_failure) = true
and if I created a local Mock object with invalid expectation call then I will get exception in exception:

  1. Because of invalid arguments passed to the method.
  2. During stack unwinding we will destroy Mock object and will call VerifyAndClearExpectationsLocked(); from ~FunctionMockerBase()
    These behavior obviously leads to process termination.

The suggestion is to check whether we already in unhandled exception or not before Verifing expectations in destructor.

There is a problem happened when GMock is used with 3rdParty UT-engine based on exceptions handling.

If I enables throwing exceptions from GMock: ::testing::GTEST_FLAG(throw_on_failure) = true
Then in test case if I created a local Mock object with invalid expectation call then I will get exception in exception:

Because of invalid arguments passed to the method.
During stack unwinding we will destroy Mock object and will call VerifyAndClearExpectationsLocked(); from~FunctionMockerBase()
These behavior obviously leads to process termination.

The suggestion is to check whether we already in unhandled exception or not before Verifing expectations in destructor.
@rgolovanov
Copy link
Author

@BillyDonahue could you please review these few lines.

@lor-anonymous-spirit
Copy link

@rgolovanov, he merges google's internal changes only, don't panic

GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
MutexLock l(&g_gmock_mutex);
VerifyAndClearExpectationsLocked();
if(!std::uncaught_exception())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is deprecated in C++17. I wonder if that will affect us.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, it is not truth. In C++17 the return type is changed only:
cppreference.com

bool uncaught_exception();
(1)	(deprecated in C++17)
int uncaught_exceptions();
(2)	(since C++17)

(2) Detects how many exceptions in the current thread have been thrown or rethrown and not yet entered their matching catch clauses.

So the proposed change is correct.

Copy link

@lor-anonymous-spirit lor-anonymous-spirit May 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name changed: uncaught_exceptionS

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, yep. I missed it first time. Should I modify this fix using GTEST_GCC_VER_ to support both versions of C++?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imho it's better to use __cplusplus macro(std, §16.8)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, see update

std::uncaught_exception has been modified to std::uncaught_exceptions in c++17, therefore we have to split fix for c++ versions before and after c++17
@rgolovanov
Copy link
Author

@BillyDonahue, @lor-anonymous-spirit could you please advice somebody who can review and accept this pull request.

@thejcannon
Copy link
Contributor

As a general rule of thumb, you shouldn't rely on std::uncaught_exceptions().
See http://www.gotw.ca/gotw/047.htm for an explanation of why it can lead to buggy code.

#else
const bool inside_uncaught_exception = std::uncaught_exceptions() > 0;
#endif
if(!inside_uncaught_exception)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space after 'if'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why it's okay to not VerifyAndClearExpectationsLocked if we're inside an unhandled exception? I'm concerned that this conditional evaluation will make an invariant more complex.

MutexLock l(&g_gmock_mutex);
VerifyAndClearExpectationsLocked();

#if __cplusplus < 201703L
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is this magic number from? I can't find a settled numeric code for C++17.

#if __cplusplus < 201703L
const bool inside_uncaught_exception = std::uncaught_exception();
#else
const bool inside_uncaught_exception = std::uncaught_exceptions() > 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be more elegant as a function than as a variable.

const bool inside_uncaught_exception = std::uncaught_exceptions() > 0;
#endif
if(!inside_uncaught_exception)
VerifyAndClearExpectationsLocked();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent by 2, not 4.

#if __cplusplus < 201703L
const bool inside_uncaught_exception = std::uncaught_exception();
#else
const bool inside_uncaught_exception = std::uncaught_exceptions() > 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't really the intended use of std::uncaught_exceptions(). To use std::uncaught_exceptions() appropriately, you should capture the number of uncaught exceptions on object construction and check again at destruction. If the number at destruction is different (it should always differ by 0 or 1) then you know you can't throw.
See the proposal.

Also worth mentioning, in the proposal is implementations for std::uncaught_exceptions() for older versions of MSVC and gcc/clang. Perhaps googletest should attempt to use those instead of defaulting to std::uncaught_exception.
See this github repo

@gennadiycivil
Copy link
Contributor

@rgolovanov Sincere apologies for the long wait. I see unfinished discussion. If this i still relevant could you please rebase and we can try to move forward from here/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants