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 new test application for import backend #650
Conversation
This is a first draft of the new test application and is work in progress. Feel free to comment on it. I'm using GMock here the first time for any GnuCash test as I can see. It was quite useful to simplify the test setup. I have no idea, if the Additionally It wasn't clear to me, what headers to include into |
Test failure is due to emitting |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear to me that this does anything beyond exercising your mocks. Perhaps that's the intent at this stage?
|
||
extern "C" | ||
{ | ||
#include "cashobjects.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use angle-brackets to include filepaths not relative to the current directory. It is an incredibly common misconception that angle brackets are for system includes and quotes for project includes: A cursory examination of the compiler manual shows that quotes mean to search the current directory before the include paths while angle brackets go straight to the include paths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, done
{ | ||
#include "cashobjects.h" | ||
#include "gnc-locale-utils.h" | ||
#include "../import-backend.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although it's common to use this idiom in GnuCash tests I think it better to put the module-under-test on the include path and use angle-brackets as a relative path buried in an include statement might turn out to be brittle in the face of a code reorganization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
|
||
|
||
// matcher to check for duplicates in containers | ||
MATCHER(HasDuplicates, "has " + std::string(negation ? "no " : "") + "duplicated elements") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Say in the comment that it's a GMock MATCHER
so that future us will know where the macro comes from.
This won't compile on all supported compilers (it really shouldn't at all), const char* has no operator+(). You want std::string("has ") + (negation ? " no " : "") + "duplicated elements"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed comment.
Isn't a double quoted string assumed to be std::string
in a *.cpp file? I copied from the example in GoogleMock CookBook.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, absolutely not. A double-quoted string literal is of type const char*
and it's instantiated in the code segment. std::string
objects must be declared as such or come from a function returning it.
using namespace testing; | ||
|
||
// unset bayesian import matching in preferences | ||
EXPECT_CALL(*m_prefs, getBool(StrEq(GNC_PREFS_GROUP_IMPORT), StrEq(GNC_PREF_USE_BAYES))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EXPECT_CALL()
needs to go in each test, not in the fixture, so that it's in the right stack frame. Perhaps you want ON_CALL()
to set up the mock's behavior?
I do wonder if .Times(AnyNumber())
here is what's leading to the infinite loop that causes the test to fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is discussed in GoogleMock CookBook. One can also use ON_CALL()
, but in this a "Uninteresting mock function call" message is printed (see last paragraph). For me both opportunities are ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That doesn't say to put EXPECT_CALL
in a fixture. In fact is says the opposite: A good design will use ON_CALL
in the fixture to define the mock's behavior and EXPECT_CALL
in the TEST_F
to test that the code under test does in fact exercise that behavior.
|
||
Account *dest_acc1 = CreateAccount("Destination 1"); | ||
Account *dest_acc2 = CreateAccount("Destination 2"); | ||
Transaction *trans = CreateTransaction("This is the description", 1.0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating the accounts and the transaction is supposed to go in the fixture's SetUp function, and deleting them in the TearDown function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a requirement in GoogleTest or a convention? I'm still learning to write tests with GoogleTest. IIUC you only add things to the fixture, if you want to use them in more than one test. At the moment the two accounts and the transaction are intended for this one test only.
But of course they have to be deleted, whether here or in the fixture's TearDown()
function. I still have to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a requirement of basic C++. Any object that allocates should be created in the fixture's setup and freed in the fixture's teardown because those functions are guaranteed to run even if the test throws an exception, whereas deallocation in the test function might not run if the function exits for some reason before the resources are freed. That's different from C test frameworks like GLib's because only a crash will prevent the function from going all the way through.
No, that's not correct. Have a look at the test summary, test 88/133 test-import-backend fails. And that's expected, since among other things this test checks for bugs, which should be fixed by PR #641. That means, after merging PR #641 test-import-backend should pass as well. The problem is, that due to this massive log output the details of test-import-backend are not visible. The log stops at test 23/133 test-backend-dbi. Do you have any idea, what's wrong? New test-import-backend actually shouldn't influence any of the other tests. |
My comments were based on code review, not on trying to run the tests.
from test-app-utils and test-backend-dbi. The reason it's blowing up Travis on Arch is because the tests are getting run there with logging turned way up and when the tests fail the testlog is printed to stdout so that we can have a chance at understanding why. It's not obvious to me where that setting is, perhaps @gjanssens knows, but while it's probably worth it to get detailed info for troubleshooting failures we need to stifle some messages and that's the #1 candidate. The actual failure on Ubuntu is
However, when I tried to run it on MacOS using clang I get a segfault:
I debugged it and found that the underlying problem is
and
That kvp_data looks like
Those addresses aren't in the legal address space so when I suspect that it doesn't segfault with gcc it's getting initialized to all 0s and not getting initialized on clang (which would be a bug because according to the standard everything except POD should be initialized to 0 unless there's a constructor to do something different). Even if it is a clang bug I don't think that googlemocking a C struct is going to work: I think googlemock is intended to mock C classes and in particular instance member functions, so that failure in Ubuntu above is because there is no code anywhere that calls I'm travelling this week with no access to my Linux VMs so I can't test that hypothesis any further until I return Sunday and I can't promise to have time to pursue it before the 3.9 release at the end of March. |
Unfortunately I don't know how to suppress those messages. I have spent a few hours this morning figuring out how to do this but didn't get anywhere. What I did manage was to simplify the error output by changing our travis test script: the script will now only output the logs from failed tests, not all tests. That should already help in many cases (like yours). If you rebase your branch on current maint and then force push it to your github repo, the new travis build log should be much reduced. |
@gjanssens that's a nice improvement. Thanks. |
ba84626
to
1f449f7
Compare
I rebased my branch on current maint. But since PR #641 has been merged now, new test-import-backend shouldn't fail anymore. And since PR #641 has been merged just before changes from @gjanssens, there is no commit, which could be used to check current travis behaviour in case of a failed test. |
Wrong, the rebase was sufficient to trigger CI and it now passes. That doesn't affect any of my comments and until you address those, most especially the incorrect use of googlemock with GncImportMatchMap this isn't going anywhere. |
1f449f7
to
7d728d2
Compare
Regarding I'm aware that mocking a C struct as a C++ class to work with GoogleMock is not the standard case. But I guess that all new tests should be written with GoogleTest/GoogleMock anyway. I tried a lot to get this test run and hope it wasn't for nothing. Mocking |
You're passing a ptr to your mock to GMock is clearly not intended to mock C structs or GObject objects (especially not GObject objects!) and I don't think that trying to use if for that is a good design. |
And how is that supposed to happen? You don't do so explicitly and providing a new function definition in your test program isn't going to magically re-link libgncmod-import-generic.so so that |
I don't think that this is magic. Original function At least this works for me and even on Travis CI. Otherwise the test would fail, since it expects the mock functions Finally if linking the mock functions instead of the original functions can not be safely controlled, it would be quite hard to mock any C function regardless of the used framework. |
Sorry, my comment about magic was a bit harsh. You're probably familiar only with the gcc linker.
That's how it works on Linux. It's not how it works on MacOS, which generates two-level symbols that contain both the function name and the shared library that it was found in at link time. Here's the relevant frames from the debug trace:
The way around that (and actually good practice in general for test programs) is build the test program from the source files that you're testing. It's pretty painful when the code you're trying to test has lots of dependent libraries, but it's what you have to do. |
I agree, but this make things more complicated now. Function |
|
||
|
||
// matcher to check for duplicates in containers | ||
MATCHER(HasDuplicates, "has " + std::string(negation ? "no " : "") + "duplicated elements") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, absolutely not. A double-quoted string literal is of type const char*
and it's instantiated in the code segment. std::string
objects must be declared as such or come from a function returning it.
using namespace testing; | ||
|
||
// unset bayesian import matching in preferences | ||
EXPECT_CALL(*m_prefs, getBool(StrEq(GNC_PREFS_GROUP_IMPORT), StrEq(GNC_PREF_USE_BAYES))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That doesn't say to put EXPECT_CALL
in a fixture. In fact is says the opposite: A good design will use ON_CALL
in the fixture to define the mock's behavior and EXPECT_CALL
in the TEST_F
to test that the code under test does in fact exercise that behavior.
|
||
Account *dest_acc1 = CreateAccount("Destination 1"); | ||
Account *dest_acc2 = CreateAccount("Destination 2"); | ||
Transaction *trans = CreateTransaction("This is the description", 1.0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a requirement of basic C++. Any object that allocates should be created in the fixture's setup and freed in the fixture's teardown because those functions are guaranteed to run even if the test throws an exception, whereas deallocation in the test function might not run if the function exits for some reason before the resources are freed. That's different from C test frameworks like GLib's because only a crash will prevent the function from going all the way through.
Inserting tests into spaghetti is hard, to the point that Michael Feathers wrote a well-regarded and very thick book on the topic. The least invasive approach would be to mock all of the account functions you need or to work out how to test with the existing map functions. One thing Feathers suggests is refactoring functions to reduce interdependency; for example instead of branching on |
Not good prospects. In this case I would choose your first proposal and try to mock all of the account functions. Refactoring existing GnuCash code I would leave to you core developers at the moment. And testing import backend indirectly by observing the behavior of the |
I've given it some more thought. What do you think about shifting all functions from Account.cpp related to What do you think? |
Well, if you're testing the import map functionality then it makes more sense to mock the accounting functions and to use the actual import map functions. The argument for having the gnc_account_imap functions in Account.cpp is that they modify the Account object by writing to its KVP slots. When Account gets rewritten as a C++ class then KVP will only be accessible to Account class member functions and their implementation will belong in Account.cpp (or maybe Account-impl.cpp if we decide that a pimpl design makes sense). What would be the justification other than possible convenience in testing the gnc_account_import_map functions in having those functions implemented in a different file? I'd name such a file Account-import-map.[ch]pp so that it sorts near Account.cpp in the directory listing and to make clear that it's part of Account's implementation. I'm also a bit skeptical that it would help you much. The new file will still have to be compiled into libgncmod-engine and the compile-time dependencies will still be in the image. You'll likely wind up creating a second libgncmod-engine-test with everything but Account-import-map.cpp compiled into it. |
I'm not sure, if I understand that correctly. The intention of the
Ok, I understand. Makes sense.
You're right. I've seen that problem now as well. Therefore I'll follow your advise and mock |
// main test function | ||
int main(int argc, char **argv) | ||
{ | ||
testing::InitGoogleTest(&argc, argv); | ||
|
||
qof_init(); | ||
if (!cashobjects_register()) | ||
exit(1); | ||
|
||
return RUN_ALL_TESTS(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need a main(). Googletest takes care of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's correct if only testing::InitGoogleTest()
and RUN_ALL_TESTS()
is called. Here I additionally wanted to initialize QOF and call cashobjects_register()
.
When I'll mock libgncmod-engine
completely now, this will not be necessary anymore, I guess, and main()
function can be omitted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're building it with gnc_add_test()
in CMakeLists.txt. That will set up the program for you and call those functions. Having your own main
should fail to compile because you're not allowed to have two.
GoogleTest provides a Global SetUp and TearDown facility where you can do things like call qof_init
and cashobjects_register
. But calling either of those gets you back to linking libgncmod-engine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, good to know.
You need to mock the functions that the function being tested calls. E.g. if you're testing |
Ok, I'll mock all required functions from
My first idea behind using GoogleTest/GoogleMock although it's not C++ code to be tested/mocked, was that you don't need to rewrite the test code completely in the future, when the code to be tested is ported to C++. Would you agree to using GoogleMock instead of another C mocking framework in this situation. |
Like I said, use GMock if you can make it work, otherwise you'll have to write the mocks by hand. We don't want to add a dependency on a C mocking framework. |
7d728d2
to
23b2132
Compare
I finally made it after some time. I have (with a few small exceptions) completely replaced I have identified two kinds of mockups so far:
The first kind of mockups is still quite easy to realize. The second kind, GObjects, was a real challenge and that's what I have been working on for the longest time. But although I had the same impression as you at the beginning that GoogleMock is not desigend for mockups of GObjects, I have changed my opinion in the meantime (see below). The GObject framework would in theory be even better suited for mockups using GoogleMock than simple functions on C structs, if the concept of GObject interfaces is used. But as far as I have seen, these are not used in GnuCash. The main problem is, that the GObject framework is quite complicated. Here are my approaches now. To mock a function operating on simple C structs as objects (first kind) I did the following:
Now to the second kind of mockups. For this I had to get familiar with the GObject framework documentation at first. I had not developed with the GObject framework before. I even looked at the GObject source code to understand how GObjects are implemented. I also looked for instructions and practical examples for writing C++ bindings on GObjects. But everything I have discovered so far has not helped me. Small fun fact: When I typed the terms "gmock" and "GObject" into my search engine, I had this GnuCash Wiki link as third hit in the hit list. Basically the approach for GObjects is an extension of the first approach. One difference is that GObjects must be allocated and initialized using the function
This is a first draft and not a finished concept. I am aware that this does not represent all aspects of GObject framework by far. For instance at least the concept of object properties, which are used in many places in GnuCash code, is missing. But for my test application these are not needed yet. And I think the approach is extensible in this respect. And finally some general thoughts about the use of GoogleMock for GnuCash code. It is correct that GMock is primarily intended for mockups based on interfaces (interface classes). And GnuCash code does not use this design so far. But this is actually not a requirement, it only concerns the way of connecting mock objects to existing code. Using interfaces and virtual methods, real objects can be replaced by mock objects dynamically at runtime. Without virtual methods, on the other hand, it must already be determined at compile time whether a mock object or the real object is to be used. This is described analogously in the section "Mocking nonvirtual methods" in the docs. As far as I can evaluate so far, there is no restriction concerning the usable functionalities of GoogleMock when mocking nonvirtual methods, only the build process is different. Another question is whether GMock is suitable to mock C code. The answer to this question depends in my opinion on whether it is possible to write a suitable C++ binding for the C code. If the C code follows an object-oriented design, one important prerequisite is fullfilled for instance. And so far it seems to be possible to use GoogleMock for GnuCash code. |
23b2132
to
98c877a
Compare
Hi @jralls, do you have any feedback for me regarding my proposal on using GMock for GnuCash? |
@gruberchr Sorry, I've been busy on other work.
That's true for GObjects that are correctly written to use the GObject memory management facilities. It's unfortunately not true of anything in the GnuCash engine.
That doesn't make sense. C functions are directly callable from C++. Do you mean that it depends on whether it's possible to coerce GMock to create a function with |
Ok, not good and might be a problem. But does this really matter, if the real implementation shouldn't be used anyway, but should be replaced by a mock up?
No, the focus of my statement is on "suitable". It doesn't make sense to use GoogleMock, if for instance the C code to be mocked does even not follow an object-oriented design. It's a basic concept of GoogleMock to mock classes. Of course you even could create a completely new GMock class for mocking plain C functions. But is it meaningful to use GoogleMock in this case? |
I have to back off and revise that to "many things", because some do fake it as you found with Account, Transaction, and Split: There's no ref counting but the "xaccFooDestroy" functions unref the GObject instance. It seems to be good enough.
That's the harder question. You've made it work for a couple of functions but it took a ton of code and a lot of hours to get there. GObject classes sort of behave like real classes through a lot of brute force and you've managed to duplicate enough of that brute force to make GMock work well enough for a couple of simple wiggle tests. Is it worth it? Do you think that you've built a base on which you can easily test the more complicated functions? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall a remarkable manipulation. It needs some more comments explaining what's going on so that one doesn't have to spend a lot of time figuring it out when one wants to work on it.
I do wonder how well this will expand into more complete testing of the import core and made worthwhile to use elsewhere in GnuCash.
Also it needs to be rejiggered to merge into master
. It's too late for maint
at this point, and besides you've used C++14 delete
and maint
is specced for C++11.
GList *tokens, | ||
Account *acc) | ||
{ | ||
// \todo use std::list instead of std::vector, since GList is a double-linked list like std::list |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not a good reason to use std::list
. The whole point of mocking is to ignore the mocked class's implementation details... and if you ever have to pass a GList*
it won't help to have a std::list
.
Does GMock require that its generated findAccountBayes
take a std::vector<std::string>
? If not you might as well make it a std::vector<const char*>
and save the std::string
allocation and construction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not a good reason to use
std::list
.
Ok, I see.
Does GMock require that its generated
findAccountBayes
take astd::vector<std::string>
?
No, it doesn't. GoogleMocks string matchers accept both. I'll change it to std::vector<const char*>
, makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in PR #738
static void | ||
gnc_mock_account_init (MockAccount *inst) | ||
{ | ||
// function is unused, initialization is done in the MockAccount's constructor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not quite true. It and gnc_mock_account_class_init
are called by G_DEFINE_TYPE. You could as easily do the work here as in the C++ ctor... and note that for GObject foo_init
and foo_class_init
the constructors; you should say "MockAccount's C++ constructor".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course these two functions are called during GObject instantiation. The comment should say, that the C++ ctor is used for instance initialization instead of this function.
Is it clear enough, when I change "MockAccount's constructor" to "MockAccount's C++ constructor" then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in PR #738
MOCK_METHOD2(findAccount, Account *(const char*, const char*)); | ||
MOCK_METHOD3(addAccount, void(const char*, const char*, Account*)); | ||
MOCK_METHOD1(findAccountBayes, Account *(std::vector<std::string>)); | ||
MOCK_METHOD2(addAccountBayes, void(std::vector<std::string>, Account*)); |
There was a problem hiding this comment.
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 be passing the vectors around by value instead of by reference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to work. I'll change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in PR #738
void | ||
xaccAccountBeginEdit (Account *account) | ||
{ | ||
g_return_if_fail(GNC_IS_MOCK_ACCOUNT(account)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ISTM you should use EXPECT_TRUE()
or maybe ASSERT_TRUE
instead of g_return_if_fail
. The latter will just write a message to std::cerr
instead of causing the test to fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I'll change it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in PR #738
#ifndef GMOCK_GOBJECT_H | ||
#define GMOCK_GOBJECT_H | ||
|
||
#include <glib.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also #include <glib-object.h>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I'll add this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in PR #738
{ | ||
public: | ||
MockPrefsBackend(MockPrefsBackend const&) = delete; | ||
MockPrefsBackend& operator=(MockPrefsBackend const&) = delete; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid the singleton pattern and lots of others..
There's no benefit to this being a singleton or even single-instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The underlying implementation of the preferences backend follows a singleton pattern by using global pointer prefsbackend
. Therefore the mock up has to use it as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uh, that's not a good reason: The whole point of a mock is to avoid implementation details, and the global variable is just that: An implementation detail.
A better answer would be something along the lines of "gnc_prefs_foo needs some way to access the preferences and since it doesn't have a preferences parameter it needs something internal and a singleton is an easy way to do that." Except that you're not using the singleton for that, you're using a global variable for it and doing so in a poorly thought out way.
The global belongs to gmock-gnc-prefs.c so it could be static with a setter . MockPrefsBackend
can be an ordinary class with a default constructor and destructor. Let there be static MockPrefsBackend* prefsbe
and gnc_prefs_set_backend(MockPrefsBackend*)
. Your fixture code would do
SetUp ()
{
gnc_prefs_set_backend(&m_prefs);
// ...
}
MockPrefsBackend m_prefs;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yes, I haven't thought again about this implementation. I used the singleton pattern in my first approach, where it was really necessary, since I linked libgnc-core-utils.so into the test application, which contains gnc-prefs.c and defined global pointer prefsbackend
.
Now this is not necessary anymore and could be changed as you suggested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in PR #738
} | ||
|
||
// This is a reimplementation of the function from qofinstance.cpp | ||
// with calling qof_instance_set_dirty() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean without calling qof_instance_set_dirty()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yes, right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed in PR #738
} | ||
|
||
// mock up for QofQuery | ||
// hint: class QofMockQuery can not be derived from QofQuery, since struct _QofQuery is not public |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Say "Note:" or "N.B." (nota bene, latin for note well) instead of "hint:".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in PR #738
@@ -0,0 +1,68 @@ | |||
#include <config.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the terms defined in GMock docs this is a fake-qofquery rather than a mock-qofquery, and gmock
doesn't have anything to do with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I'll check that. Do you have a proposal for a naming convention?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fake-qofquery.h
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in PR #738
|
||
|
||
|
||
/* mock functions, which can not be mocked by mock classes */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They're fakes/replacements, not mocks, and the comment should say why they're needed, e.g. being defined in a library we don't want to link to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point is that the comment needs to say that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed in PR #738
What do you think? It's surely not worth it for only a handful of tests. If it helps to write a lot of more tests, which are not possible to write up to now with reasonable effort, it makes sense I think.
I can not answer this question. I don't know the GnuCash code as good as you. But I hope so.
I was expecting this.
I fully agree. When I started writing this test, I hadn't thought, that it gets that complicated at the end. But all the work has been done and it would be a pity, if all the hours were in vain. As you wrote as well, it's a proof-of-concept for introducing mockups into GnuCash test code. Therefore I'm asking you know to decide how to proceed. I guess it would be helpful, if you prepare a proposal, where to put all the mock up code and what to change based on my first draft. |
@gruberchr sorry for the delay, I wanted to get @gjanssens to have a look and consult with him about where to put the mocks. We decided that the cleanest location is a I've made the necessary changes in the merge commit and merged it into master. Thanks! |
Thanks @jralls for merging. As I have seen, you did not touch the code, but only moved the files in the merge commit. What about your open remarks above? Change it now or leave it? |
Please answer the questions and proceed to fix the rest. I merged because none of them are real show-stoppers--this is after all test code--and you seemed to be waiting for something to happen with regards to moving branches. |
Hi @jralls can you or @gjanssens have a look at PR #738? It doesn't influence the GnuCash 4.0 release. |
@gruberchr You can just ping on the other PR. I'll pick up reviewing the open PRs in a week or two, I've been focussing the last month on getting 4.0 ready for release so I have a pile of non-GnuCash work that needs my attention. |
fixes longstanding unreported bug - quarter/halfyear were not being handled!
This new test is motivated by PR #641. Two of the bugs fixed in this PR had no test application before.