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

add support for long long integers as native types #1142

Merged
merged 9 commits into from
Aug 12, 2018

Conversation

jacob-keller
Copy link
Contributor

When testing code that needs to work on both 64-bit platforms and 32-bit
platforms, the long integer may not guarantee 64bits of width on the
32-bit platform. This makes testing calls which take and return 64bit
values difficult, as the test framework can't handle these natively.

Add support for long long integers, so that test code can natively
handle these larger values.

Signed-off-by: Jacob Keller jacob.e.keller@intel.com

@jacob-keller
Copy link
Contributor Author

I think more tests is probably a good idea, but wanted to get this up as a pull request to gauge whether there was interest.

@coveralls
Copy link

coveralls commented Feb 27, 2018

Coverage Status

Coverage increased (+0.004%) to 99.878% when pulling 233f864 on jacob-keller:jk-long-long-support into 03fabe0 on cpputest:master.

@basvodde
Copy link
Member

@jacob-keller . I appreciate the pull request. So far, all the long long usage is under a precompiler define so that it works also on old systems without a long long. That is why, so far, we've not accepted yet the mocking support for long long because adding #if in a class with virtual methods is dangerous.

Any alternative suggestion is welcome though!

@jgonzalezdr
Copy link
Contributor

Perhaps shouldn't just using cpputest_longlong and cpputest_ulonglong typedefs like in the test macros be enough?

BTW, some additional unit tests are still needed to cover all the new functions.

@jacob-keller
Copy link
Contributor Author

BTW, some additional unit tests are still needed to cover all the new functions.

Yea I can work on that part, I found some more issues.

I think the typedefs won't work properly because you'd end up masking functions when the type doesn't exist, no?

@basvodde
Copy link
Member

Previous attempt to typedef's didn't work.

@jgonzalezdr
Copy link
Contributor

@basvodde : What is the problem with #ifdef'ing virtual functions? CI builds with C++11 could be used to give early error detection if a override is defined on a subclass but not on the parent class, and also if a override is not defined properly with the override keyword, it's just a matter of adding a C++11 compilation with and without long longs to ensure that functions are defined coherently.

@basvodde
Copy link
Member

basvodde commented Mar 1, 2018

@jgonzalezdr The problem is that when you accidentally include one header without the define, then your code will accidentally call the wrong method due to the different vtables. It leads to potentially really painful bugs. In general, it is best to avoid #ifdefed virtual functions.

@jgonzalezdr
Copy link
Contributor

@basvodde I more or less see the problem. Why not make the inclusion of CppUTestGeneratedConfig.h to be mandatory in CppUTestConfig.h (i.e. remove #ifndef CPPUTEST_USE_OWN_CONFIGURATION)? The install scripts always install CppUTestGeneratedConfig.h, therefore the generated libraries and its associated includes would always be "synchronized". There would be no way (expect unwise tampering by the user) to accidentally compile a test using a configuration different than the one used to compile the library, isn't it?

@jacob-keller
Copy link
Contributor Author

Is there a good way to determine test coverage locally before pushing? I want to make sure I've added tests to cover everything I've changed since the previous push.

@basvodde
Copy link
Member

basvodde commented Mar 2, 2018

@jgonzalezdr That won't work, especially because it will cause a different binary and many people (including me) pick up CppUTest via a package manager.

@jgonzalezdr
Copy link
Contributor

@basvodde But package managers install both include files and binary libraries, therefore if CppUTestGeneratedConfig.h is part of the distribution and is mandatory to exist, there should be no problem, isn't it? I mean, I've used some libraries (e.g. wxWidgets) that #ifdef virtual functions and use a config header specific to the generated binary without any problems, and also didn't notice any users having problems. I think I may be missing the point on a use case that I've never been though... 😕

@basvodde
Copy link
Member

basvodde commented Mar 2, 2018

@jgonzalezdr I'm skeptical and careful as the problems it can lead to are so hard to debug. If the header and the binary are different then, in the debugger, all the function calls will go to another function with a messed up stack. It is not nice. If we can avoid that, that is better. I'm not sure this functionality is worth the potential pain.

@jacob-keller
Copy link
Contributor Author

I'm skeptical and careful as the problems it can lead to are so hard to debug. If the header and the binary are different then, in the debugger, all the function calls will go to another function with a messed up stack. It is not nice. If we can avoid that, that is better. I'm not sure this functionality is worth the potential pain.

So if I actually need access to mock functions of these types what is the best way?

@basvodde
Copy link
Member

basvodde commented Mar 2, 2018

I do not know what the best way is. We had a similar pull request before and decided not to merge that due to the #ifdef around the virtual functions.

@jacob-keller
Copy link
Contributor Author

I do not know what the best way is. We had a similar pull request before and decided not to merge that due to the #ifdef around the virtual functions.

I don't mean "what shoudl I do to make this includable" since I think there's no other solution besides either (a) require that all compilers support longlongs, or (b) use an #if macro.

What I mean is, "what's the best way to mock a function that takes a long long given that the built in types don't work? Do I have to make a comparator?

@basvodde
Copy link
Member

basvodde commented Mar 2, 2018

@jacob-keller I think a comparator might actually work. You could then also add convenience methods in or out of the class (as non-virtual) and put these in #ifdef!

@jacob-keller
Copy link
Contributor Author

@jacob-keller I think a comparator might actually work. You could then also add convenience methods in or out of the class (as non-virtual) and put these in #ifdef!

Yea I'm gonna see if I can do this as a sort of built-in comparator.

@jacob-keller
Copy link
Contributor Author

Yea I'm gonna see if I can do this as a sort of built-in comparator.

I don't see any obvious way to install a default comparator, but we could use a non-virtual .withParameter overload to call .withParameterOfType("type" ...)

So I think this is the best solution forward if we can find a way to get the comparator installed by default.

@uecasm
Copy link
Contributor

uecasm commented Mar 2, 2018

I apologise for going dark on #847 for so long, but that solution just works for me and I've been distracted by other things. If there's still interest in the idea (using #if but providing stubs so the vtable stays in sync) I could look at cleaning up and updating the remaining unmerged parts of that PR?

@jgonzalezdr
Copy link
Contributor

jgonzalezdr commented Mar 3, 2018

I've pushed a PR on @jacob-keller own branch (jacob-keller#1) that avoids the vtables problem by using a intermediate subclass to contain all the #ifdef'd functions.

That way, as we have simple inheritance, if compiling without long long support, the vtable for the base class will be the same and as expected by the compiler.

If both of you like this solution, just accept the PR on Jacob's repo.

@jacob-keller
Copy link
Contributor Author

Thanks, I'll take a look once I get some more time, probably Monday.

@basvodde
Copy link
Member

basvodde commented Mar 4, 2018

@jacob-keller The comparator can be installed in the convenience methods that are outside of the class?

@jgonzalezdr
Copy link
Contributor

There is still another way to avoid the vtables problem without making "special" designs or treating long longs in a different ways, just using #ifdefs:

Two functions can be added, one inline and the other non-inline that just detect the current compilation flags (for now with long long support could be enough maybe). The mock framework could receive in some key function (e.g. MockSuppport's mock()) the result of the inline version as a default value, and then it can just compare that both functions return the same values and throw an error if not.

@basvodde
Copy link
Member

basvodde commented Mar 4, 2018

@jgonzalezdr I'm quite comfortable with the idea of using a comparator. In fact, almost wondering whether we need to to that for all types :)

I don't think we want to add detection. It is best to just avoid #if.

@jgonzalezdr
Copy link
Contributor

@basvodde Using comparators for everything sounds good, at least the implementation would be homogeneous, and CppUTest is already inefficient because it does string comparisons for the type all the time. But if the capability of comparing integers of different sizes has to be maintained, the comparator design will have to be improved such that the "other" type is also passed and the comparator decides if one type can be compared with the other. This may break backward compatibility with already existing user comparators.

@jgonzalezdr
Copy link
Contributor

As per my previous comment, just in case the comparator strategy is not feasible or whatever, I've revamped my previous PR to avoid subclasses (jacob-keller#2), leaving everything much cleaner.

@jacob-keller
Copy link
Contributor Author

jacob-keller commented Mar 4, 2018 via email

@jacob-keller
Copy link
Contributor Author

Found another miss, the MockSuport() didn't have cpputest_longlong and cpputest_ulonglong functions, added those

@jacob-keller
Copy link
Contributor Author

Ok, I think I finally got coverage for all the lines, might be missing a couple in the C test files. It's hard to test it manually since I can't get the coverage report working locally.

@basvodde
Copy link
Member

Got some failing tests now though.

@jacob-keller
Copy link
Contributor Author

It looks like this might be a result of LLONG_MAX not being correct on some platforms? Hmm it seems to work on some of the platforms, but not others?

@jacob-keller
Copy link
Contributor Author

I'll check what changed between versions, see if something obvious makes sense.. The two test cases don't seem like they should fail, and I've been pouring over the MockNamedValue stuff to see if I could spot it, no luck so far.

@jacob-keller
Copy link
Contributor Author

I think I spotted what the problem was, plus a few other cleanups

@jacob-keller
Copy link
Contributor Author

It looks like this might be a result of LLONG_MAX not being correct on some platforms? Hmm it seems to work on some of the platforms, but not others?

Woops, I had accidentally used a .longIntValue_ accessor when I should have used .longLongIntValue_, which resulted on breakages on 32bit platforms.

@jacob-keller
Copy link
Contributor Author

The travis ci failure appears to be a network issue:

Connecting to ftp.openwatcom.org (ftp.openwatcom.org)|70.165.27.117|:80... failed: Connection refused.

@basvodde
Copy link
Member

I'll restart the build when it is due to network failure. But both the Appveyor and the Travis build are failing at the moment...

@@ -27,6 +27,7 @@

#include "CppUTest/TestHarness.h"
#include "MockFailureReporterForTest.h"
#include <climits>
Copy link
Member

Choose a reason for hiding this comment

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

Direct C includes are not what we want. Shouldn't happen anywhere at the moment, right? Why is this needed?

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 think we need this for the definition of LLONG_MAX and ULLONG_MAX? Maybe we don't tho?

@basvodde
Copy link
Member

Almost there. Sorry for doing a bit more requests still. Can you try to seperate out all the long long tests in one section so there is only one (or very few) #ifdef in the file?

I wanted to merge as it, but noticed that there is a C include... that is probably not what we want.

@jacob-keller
Copy link
Contributor Author

Can you try to seperate out all the long long tests in one section so there is only one (or very few) #ifdef in the file?

I will move all of them together into one block for each file, yes.

jacob-keller and others added 9 commits August 11, 2018 11:56
When testing code that needs to work on both 64-bit platforms and 32-bit
platforms, the long integer may not guarantee 64bits of width on the
32-bit platform. This makes testing calls which take and return 64bit
values difficult, as the test framework can't handle these natively.

Add support for long long integers, so that test code can natively
handle these larger values.

Note, to avoid ABI compatibility issues, use cpputest_longlong and
cpputest_ulonglong. This ensures the virtual function tables are the
same whether long long support is enabled or disabled.

With this patch, if long long support is disabled in CppUTest, then the
mock withParameter and returnParameter functions will fail indicating
that the long long support is not enabled.

Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
Use long long int, instead of long int, and type cast only when we are
converting from a signed to an unsigned type, or vice versa.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
Otherwise we will inadvertently return a potentially smaller sized
value, resulting in incorrect return values.

This fixes tests on various 32 bit platforms.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
We incorrectly accessed the longLongIntValue_ section of the union when
comparing a lont int and an unsigned long long int.

This resulted in incorrect behavior on some 32bit platforms. This is
likely due to the union not being zero initialized.

Fix this by using the correct accessor, longIntValue_.

Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
It was originally added due to thinking we needed it for ULLONG_MAX
definitions. Technically, we probably *do* need it, but it likely gets
included through some other file.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
Combined all of the mismatched long long and other type tests into one
block. This reduces the number of checks for CPPUTEST_USE_LONG_LONG, and
hopefully makes the code easier to read.

I chose not to move the expectOne*ParameterAndValue tests, because these
are not related to the mismatched type tests, and I wanted to keep
similar tests together. Moving them would make the test layout a bit
more confusing.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
@jacob-keller
Copy link
Contributor Author

Ok, I removed the climits header, and I re-ordered some of the tests in MockParameterValue.cpp

I chose not to re-order EVERY file to combine all the tests in one location, because I felt that the tests didn't go well together. Essentially, I think it's better to keep the tests which are similar to regular typed tests near each other, rather than reduce one or two #ifdefs.

If you feel very strongly, I will make that change, but I'd prefer not to.

@basvodde
Copy link
Member

Test failure on cygwin Legit?

@jacob-keller
Copy link
Contributor Author

Hmm.. Itt's failing in TestDefaultCrashMethodInSeparateProcessTest which I don't think I modified?

Hmm

@basvodde
Copy link
Member

Restarted and it works. Merging.

Thanks for your patience and contribution!

@basvodde basvodde merged commit 6481ebc into cpputest:master Aug 12, 2018
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

Successfully merging this pull request may close these issues.

None yet

5 participants