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

Fix MOCK_METHOD to handle noexcept correctly #2498

Merged
merged 6 commits into from
Oct 11, 2019

Conversation

thejcannon
Copy link
Contributor

Godbolt link to the preprocessed output of using the macro:
https://godbolt.org/z/rptA9J
(Scroll down to the bottom of the preprocessed output to see it parrotting the noexcept specifier verbatim)

Fixes #2472

Let me know if y'all can think of any other test cases to throw at it.

@thejcannon thejcannon changed the title Noexcept spec Fix MOCK_METHOD to handle noexcept correctly Oct 7, 2019
Copy link

@ugoren ugoren left a comment

Choose a reason for hiding this comment

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

The fix seems OK, and certainly improves behavior.
I suggested some improvements to tests.
Another small issue is behavior in case noexcept is used multiple times (which is invalid usage). I think it will lead to cryptic error messages, and better validation would be nice. But it isn't critical.

};

TEST(MockMethodMockFunctionTest, NoexceptSpecifierPreserved) {
EXPECT_TRUE(noexcept(std::declval<MockMethodNoexceptSpecifier>().func1()));
Copy link

Choose a reason for hiding this comment

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

Is there a reason to use declval here? I'd simply define a MockMethodNoexceptSpecifier object.
In case the compiler warns that it's unused, it can be suppressed by cast to void.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had that previously, but the compiler would always say the result of the expression is noexcept(false).
https://travis-ci.org/google/googletest/jobs/594800623#L2313
My guess would be it has to do with the constructor not being marked noexcept.
But declval really is the "correct" solution as we just want to test the noexcept specifier for the function (and not the default constructor as well).

Another solution would be to use std::is_nothrow_invocable, but I don't think there's a technical reason to prefer one over another.

Copy link

Choose a reason for hiding this comment

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

My intention was to define one MockMethodNoexceptSpecifier object in the test, and use it in all expectations. Then it wouldn't matter if the constructor is noexcept.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's definitely another solution. Personally, I wouldn't like having to declare an instance of an object (which is a runtime construct) just so syntactically (since noexcept expressions are evaluated at compile-time) I can reason about the methods.
But I also can't argue it would be less code.
If this comes up in the "internal review" I'd be alright changing it.

Choose a reason for hiding this comment

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

Instead of EXPECT_TRUE/FALSE, these could be tested at compile time with static_assert.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it's all about tradeoffs if you want to use static_asserts in your test code to test some logic.
If you move it into a compile-time check, you know it'll always be true. The tests wouldn't compile otherwise. On the other hand, normally one test failing doesn't bar other tests from running and reporting their successes/failures. Since the noexcept spec wouldn't be absolutely fatal if it was wrong, I opted to implement them as runtime checks so they can report successes/failures just like other tests.

EXPECT_EQ(noexcept(std::declval<MockMethodNoexceptSpecifier>().func5()), noexcept(1+1));
EXPECT_EQ(noexcept(std::declval<MockMethodNoexceptSpecifier>().func6()), noexcept(1+1));
EXPECT_EQ(noexcept(std::declval<MockMethodNoexceptSpecifier>().func7()), noexcept(hasTwoParams(1,2)));
}
Copy link

Choose a reason for hiding this comment

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

I see no point in comparing with noexcept(1+1) - it simply evaluates to true. I suggest:

void Throws();
void DoesntThrow() noexcept;
struct MockMethodNoexceptSpecifier {
  MOCK_METHOD(void, func4, (), (noexcept(Throws())));
  MOCK_METHOD(void, func5, (), (noexcept(DoesntThrow())));
};
TEST(...) {
  EXPECT_FALSE(noexcept(mock.func4()));
  EXPECT_TRUE(noexcept(mock.func5()));
}

@thejcannon
Copy link
Contributor Author

Another small issue is behavior in case noexcept is used multiple times (which is invalid usage). I think it will lead to cryptic error messages, and better validation would be nice. But it isn't critical.

Seemingly it currently (before my change) leads to no error messages: https://godbolt.org/z/0TtYoG
So, some would argue that any error message is better than none. I'll swing back around and work on a follow-up PR that makes the error message nice. It should be easy.

@gennadiycivil gennadiycivil self-assigned this Oct 10, 2019
@gennadiycivil
Copy link
Contributor

@thejcannon I am going to import this into internal system so we can have an internal review.
273951497
Thank you, we have started internal review. Please don't push any more changes into this PR as they might be overwritten.

gennadiycivil added a commit that referenced this pull request Oct 11, 2019
@gennadiycivil gennadiycivil merged commit bc996e0 into google:master Oct 11, 2019
@gennadiycivil
Copy link
Contributor

@thejcannon Looks like this broke Windows builds.
See https://ci.appveyor.com/project/GoogleTestAppVeyor/googletest/builds/28041555

@thejcannon
Copy link
Contributor Author

@gennadiycivil
So the good news is that the bug was there before my change: https://godbolt.org/z/WbouCI
Looks like my tests just exposed them.

I'll take a look at it when I get to work today.

Might be related to #2490

gennadiycivil added a commit that referenced this pull request Oct 11, 2019
@gennadiycivil
Copy link
Contributor

In the meantime we reverted, ba513d2

@thejcannon
Copy link
Contributor Author

#2514 has been created to fix the issue.

thejcannon added a commit to thejcannon/googletest that referenced this pull request Oct 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

GMock doesn't support noexcept functions
5 participants