YASL Request
Brad Bishop
bradleyb at fuzziesquirrel.com
Wed Apr 11 13:52:11 AEST 2018
> On Apr 9, 2018, at 10:45 PM, Lei YU <mine260309 at gmail.com> wrote:
>
> On Tue, Apr 10, 2018 at 6:27 AM, Patrick Venture <venture at google.com> wrote:
>> Everyone,
>>
>> I'm working on unit-testing in openbmc, and have cracked most of
>> sdbusplus into mockable pieces and verified I can in fact test against
>> those mocks with a downstream daemon. I'll be grabbing an upstream
>
> Great! I have tried to make sdbusplus mockable previously, by changing its
> interfaces virtual, and find out that it is somehow complicated because some
> of the interfaces return the objects and it's kind of hard to mock things like
> bus::new_method_call().
> At that time I discussed this with Patrick Williams and he suggested sdbusplus
> should be compact and fast, so it's not a good idea to make it virtual.
FWIW, I agree with Patrick W’s sentiment here. I’m all for unit tests, but
un-optimizing existing code so we can use a specific framework in a specific
way doesn’t sit well with me.
> Later it's found that Brad has some good example of mocking sdbusplus in
> https://github.com/openbmc/phosphor-dbus-monitor/tree/master/src/test
Yes, I was able to mock sdbusplus with GMock without having to change sdbusplus
at all. Patrick, is something like that on the table? It seems like instead of
the typical:
class iface
{
virtual void sdbusplus_foo() = 0;
};
clase real : public iface
{
void sdbusplus_foo() {}
};
class fake : public face
{
MOCKME(sdbusplus_foo);
};
int main()
{
real r;
r.sdbusplus_foo();
}
void testcase()
{
fake f;
EXPECT_BLAH_BLAH();
f.sdbusplus_foo();
}
————————————————
we could do:
class real
{
void sdbusplus_foo() {}
};
class fake
{
MOCKME(sdbusplus_foo);
};
template <typename T>
MyClass
{
MyClass(T& interface)
{
interface.sdbusplus_foo();
}
};
int main()
{
real r;
MyClass<real> m(r);
}
void testcase()
{
fake f;
MyClass<fake> m(f);
}
————————————————
Granted this requires _all_ of our code to be templated, obfuscates what is
really going on and is all just a bit silly, but from where I sit it isn’t
any more obfuscated than wrapping every shared library function with an abstract
interface class _and_ you don’t incur the overhead of doing that. Am I overlooking
anything?
I’m only half serious with the proposal. My real point is I feel like we are
jumping through hoops to make a unit test framework work despite there being other
ways of writing unit tests.
Lei - thank you for weighing in. If this was a poll it would be:
2 in favor of becoming a GMock project at the core.
1 against
If no-one else speaks up, then I lose :-). But it would be nice to improve the
margin of error.
>
> Hopefully we can get a mockable sdbusplus as a shared library as well!
>
>> daemon (or providing a piece of one) to demonstrate how to leverage
>> the mocks to test OpenBMC. If one designs with testing in mind, the
>> designs come out very differently if not, and so getting unit-tests
>> throughout OpenBMC will be a lot of breaking things apart into
>> testable pieces. Anyways, where I'm going with this email is that
>> everything we do within a daemon needs to be something that can be
>> mocked -- basically.
>>
>> ***
>> What do I mean specifically? Consider, file access. If a daemon
>> routes all file accesses throug a common object implementation
>> provided by a shared library, that shared library can easily also
>> provide a mock interface for those accesses, such that one can easily
>> verify behaviors based on file contents without implementing local
>> files or trying to inject errors. With a mock's file system
>> interface, you can simply say that a file was unable to be read, or
>> written, or opened, etc. Another example is mocking ctime. If you
>> want to test whether something happens after some sleep or period, if
>> your code can receive a mock version of that library, one can
>> deliberately control the results of 'time' or 'difftime', etc. I have
>> to build these interfaces for some of our downstream daemons and
>> likely for other parts of OpenBMC, and to avoid code duplication it'll
>> help to have them in some library.
>>
>> YASL (yet-another-shared-library) Request.
>>
>> Previous conversations along these lines lead to the idea that we need
>> multiple new libraries for various things. So, this is yet another
>> use case. The library itself should be written in such a way that it
>> can be tested via unit-tests, but depending on how thin of a shim it
>> is, that isn't always practical. See:
>>
>> class FileInterface {
>> public:
>> virtual int open(const char *filename, int flags) = 0;
>> };
>>
>> class FileImplementation : public FileInterface {
>> public:
>> int open(const char *filename, int flags) override {
>> return ::open(filename, flags);
>> }
>> };
>>
>> class FileMock : public FileInterface {
>> public:
>> MOCK_METHOD2(open, int(const char *, int));
>> };
>>
>> .... then one just uses the FileInterface for their normally direct
>> posix-style file access. This can easily wrap iostream, or fstream,
>> or anything. And then if we provide some libraries for use by
>> daemons, they can transition over to them over time, and then they get
>> mocks for free :D For a daemon downstream, I've written a ctime
>> wrapper, I'll submit it for consideration later tonight along with a
>> few other things, and then I'll reply to this email with links.
>>
>
> So this library would contain several interfaces classes (e.g. FileInterface,
> TimeInterface, and hopefully SdbusplusInterface etc) all together, right?
> I vote for it!
>
>> ***Not meant as a unit-test primer, just trying to provide some real examples.
>>
>> Patrick
More information about the openbmc
mailing list