These are chat archives for Snaipe/Criterion

2nd
Mar 2016
Dominik
@kaidowei
Mar 02 2016 13:10
@Snaipe hi, we're you able to implement the things we talked about?
Franklin Mathieu
@Snaipe
Mar 02 2016 13:17
I actually did some huge progress
not specifying fields also works like a charm
which means that mmk_expect(foo, .param = 0) still expects 0 for param, and mmk_expect(foo) would accept any param
Dominik
@kaidowei
Mar 02 2016 13:19
wow, how did you solve that?
Franklin Mathieu
@Snaipe
Mar 02 2016 13:19
parsing, mostly
I stringify the parameters to see what gets passed
Dominik
@kaidowei
Mar 02 2016 13:20
:D
Franklin Mathieu
@Snaipe
Mar 02 2016 13:20
and initialize an offset table into the structure to match names with fields
Dominik
@kaidowei
Mar 02 2016 13:22
I find it counter intuitive, that you write mmk_mock_define (realloc_mock, void *, void *, ptr, size_t, size); to mock realloc
Franklin Mathieu
@Snaipe
Mar 02 2016 13:22
It was to prepare for harminization with other macros
I would expect mmk_mock* macros to first pass the identifier of the mock stub
but you could use a c-style declaration order, yeah
given the documentation, would you really find that counterintuitive?
Also, you still have to discriminate with the case where void is the return type
(as you can't have void as a structure field)
so both mmk_mock_define and mmk_mock_define_void share the same ordering logic
Dominik
@kaidowei
Mar 02 2016 13:26
well, I'm not talking about void *, realloc_mock vs. realloc_mock, void *but about realloc_mock
sorry for the confusion
the _mock
Franklin Mathieu
@Snaipe
Mar 02 2016 13:27
oh, right
this will probably get changed in proper samples
it's just a name though, so we're free to choose
Dominik
@kaidowei
Mar 02 2016 13:49
ah, I thought, that if we set the name there, we don't have to do that at the create time
Franklin Mathieu
@Snaipe
Mar 02 2016 13:50
that would be against reusability
Dominik
@kaidowei
Mar 02 2016 13:50
why is that?
Franklin Mathieu
@Snaipe
Mar 02 2016 13:50
actually, let me rephrase that
you need to specify the name because you need some kind of ID to refer to the declared structures, and even if you found a way to do so without, you could still want to be able to declare a mock prototype for a group of functions sharing the same prototype
mmk_mock_define defines a model, not a targetted mock
Dominik
@kaidowei
Mar 02 2016 13:58
I'm not convinced, that it is useful to reuse a defined mock multiple times
that only works for a group of functions, that share the same signature
Franklin Mathieu
@Snaipe
Mar 02 2016 13:59
why not? The behaviour of the mock stub is exactly the same
Dominik
@kaidowei
Mar 02 2016 14:00
yes, but they could stand for VERY different things.
I would not use the same definition for compare(obj1, obj2) and merge(obj1, obj2)
Franklin Mathieu
@Snaipe
Mar 02 2016 14:01
I'm mostly talking about functions made to respect a particular function pointer definition
(i.e. handler functions & predicates for instance)
but in any case, you still need to pass the mock id to mmk_mock_create
(since it needs some metadata on the mock, not just the function pointer itself)
Dominik
@kaidowei
Mar 02 2016 14:06
hmm, okay, as I'm not forced to reuse the mock definitions, I'm fine with that
for many handlers, you can save a lot of space...
mmk_expect (m, realloc_mock can't you use m to figure realloc_mock out?
Franklin Mathieu
@Snaipe
Mar 02 2016 14:08
no unfortunately, because I still need the definition of struct reallocmock_params
(for the compound literal)
Dominik
@kaidowei
Mar 02 2016 14:14
I see. If you change the mmk_mock_define to be 1:1 for a function, you could create a mmk_expect_<mymock> for each declaration and the user would not need to carry the m around
mmk_mock_create ("realloc", NULL, realloc_mock) could be reduced to mmk_mock_create(realloc)
Franklin Mathieu
@Snaipe
Mar 02 2016 14:17
bad idea
you still need to define what realloc you're changing
Dominik
@kaidowei
Mar 02 2016 14:17
what do you mean with "what realloc"?
Franklin Mathieu
@Snaipe
Mar 02 2016 14:17
is it the one used by the current program, or the one used by the library, or the one used by the libc?
Dominik
@kaidowei
Mar 02 2016 14:18
put the optional path behind realloc
Franklin Mathieu
@Snaipe
Mar 02 2016 14:18
there are as many plt addresses for realloc as there are dynamic modules
this means that you need at least two parameters
so mmk_mock_create("realloc@libc", mock_id)
for instance
Dominik
@kaidowei
Mar 02 2016 14:19
yeah, so in summary:
  • I save 1 parameter at _create
  • I do not need to carry mmk_mock m around
  • I have 1 parameter less at mmk_expect
don't think it's so bad :)
Franklin Mathieu
@Snaipe
Mar 02 2016 14:20
somethink like that, yeah
there are still some quirks, mostly in the definition part, but it's acceptable
Other API styles should also be explored
I'd like to test out cmocka's style where you implement your stub with macros
Dominik
@kaidowei
Mar 02 2016 14:24
okay. I don't like their style very much, because normally the body of a mocking function is always the same and if you need some special syntax, you can still use a custom stub
do you want to use their --wrap approach too or use your stubs?
Franklin Mathieu
@Snaipe
Mar 02 2016 14:25
our stubs
but if we implement something like that, it will probably be the same line-wise
Dominik
@kaidowei
Mar 02 2016 14:26
yes.
What still is not ideal: we still generate a mock for every function, even if we don't use an external generator
Franklin Mathieu
@Snaipe
Mar 02 2016 14:27
something like
static void *realloc_mock(void *ptr, size_t size) {
    mmk_mock_start;
    mmk_expect(NULL, ptr == NULL, size == 42);
    mmk_mock_end;
}

we still generate a mock for every function

Explain? I don't really see why that's an issue

Oh, wait, I got it
Dominik
@kaidowei
Mar 02 2016 14:29
more like? :)
static void *realloc_mock(void *ptr, size_t size) {
    return mmk_expect(NULL, ptr == NULL, size == 42);
}
Franklin Mathieu
@Snaipe
Mar 02 2016 14:29
probably not, you can't chain these statements
You should be able to do something like
static void *realloc_mock(void *ptr, size_t size) {
    mmk_mock_start;
    mmk_expect(NULL, ptr == NULL, size == 42);
    mmk_expect(NULL, ptr == (void*) -1);
    for (size_t i = 0; i < 10; ++i) {
        mmk_expect(NULL, size == i);
    }
    mmk_mock_end;
}
Dominik
@kaidowei
Mar 02 2016 14:31
okay, the generate thing:
I can't somehow say, "mock everything inside this lib/header" not a big problem, but might be annoying for big libs
that could work (with some internal state, declaring a bool var, setting it when first called, then returning)
Franklin Mathieu
@Snaipe
Mar 02 2016 14:33
How would "mock everything" work? You can't possibly define the behaviour of all functions regardless of their prototype and semantics
I've not even seen this in production tests
Dominik
@kaidowei
Mar 02 2016 14:34
but with this approach you have a mock for every test. if my first test needs the first call to check for 42 and the second for 13, I have to define two mocks for the same function
yes, I still have to fill in the values for all functions, that get called, but I'd like to avoid writing mmk_declare for every function
Franklin Mathieu
@Snaipe
Mar 02 2016 14:35
You don't have mmk_declare, that's the thing
in terms of line, it's about the same to be honest, you still have to do it somewhere
whenever it's inside a function or your test isn't that much important
Dominik
@kaidowei
Mar 02 2016 14:36
yes, the generate thing was meant for the current approach
Franklin Mathieu
@Snaipe
Mar 02 2016 14:37
the big plus here is that this API is lightweight, and you don't need to do some massive hacking with offsets to compare actual and expected values
you simply pass a predicate that references the parameters
Dominik
@kaidowei
Mar 02 2016 14:38
I guess, we could make both work together
Franklin Mathieu
@Snaipe
Mar 02 2016 14:38
that could be counterproductive
Dominik
@kaidowei
Mar 02 2016 14:39
well, both have limitations
the second works for ... functions
Franklin Mathieu
@Snaipe
Mar 02 2016 14:40
right, variadics work out of the box here
however the handling of void-returning functions is moved to mmk_expect
since mmk_expect takes in a hard value to return
you'd need to have a mmk_expect and mmk_expect_and_return
or something of that flavor
I guess, to be fair, that there could be some convenience functions of the sort for the stub API rather than the mock API
you could also decouple the return value from mmk_expect
Franklin Mathieu
@Snaipe
Mar 02 2016 14:45
which means you'd have
static void *realloc_mock(void *ptr, size_t size) {
    mmk_mock_start;
    mmk_expect(ptr == NULL && size == 42);
    mmk_return(NULL);
    mmk_expect(ptr == (void*) -1);
    mmk_return(NULL);
    for (size_t i = 0; i < 10; ++i) {
        mmk_expect(size == i);
        mmk_return(NULL);
    }
    mmk_mock_end;
}
this fixes the problem of the void-returning function handling
but it also a whole lot more verbose
Dominik
@kaidowei
Mar 02 2016 14:49
hmm that seems a bit overkill
Franklin Mathieu
@Snaipe
Mar 02 2016 14:50
I'm not that much of a fan, yeah
Dominik
@kaidowei
Mar 02 2016 14:50
mmk_expect and mmk_expect_return and mmk_return should be enough
the compiler will see some return foo for a return void function and complain