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 possibility to specify argument matchers #11

Open
tudortimi-ifx opened this issue Aug 29, 2018 · 5 comments
Open

Add possibility to specify argument matchers #11

tudortimi-ifx opened this issue Aug 29, 2018 · 5 comments

Comments

@tudortimi-ifx
Copy link

tudortimi-ifx commented Aug 29, 2018

I've run into the first situation where I would like to write a test that only checks some aspect of an argument (that it's in a certain range, for example) and not for equality with a certain value. It would be great if we could start thinking about how to specify matchers.

Maybe something like:

// specifies a matcher for each argument
`EXPECT_CALL(mock, some_func).with_arg_matchers(matcher0, matcher1)

We could implement something like a "don't care" matcher (equivalent to GMock's "_"), but the syntax will be very verbose (unless we trade off compile time safety), due to the lack of parameter inference/function overloading.

@tudortimi-ifx
Copy link
Author

Another situation I ran into where matchers are necessary is when setting expectations on args that are objects. There it's not enough to use the equality operator.

@nosnhojn
Copy link
Collaborator

correct. that's a weak compare on the reference, not a deep compare.

we have more thinking to do on the matchers. I think we agree that a simple matcher scheme ends up being the secret sauce that makes this whole idea truly useful... but keeping it simple is going to be tricky with the language constraints. sigh.

@tudortimi-ifx
Copy link
Author

tudortimi-ifx commented Aug 31, 2018

First idea:

It would be possible to define some kind of macro that hides the parameterization.

Let's say we have the following mock:

`SVMOCK(clazz_mock, clazz)
  `SVMOCK_VOIDFUNCTION3(func, int, arg1, , string, arg2, , bit, arg3, )
`SVMOCK_END

A full call that would look like:

// First argument has to greater than 5, second is not checked, third has to be equal to 1
`EXPECT_CALL(mock, func).with_arg_matchers(greater #(int)::new(5), any #(string), equal #(bit)::new(1));

The parameterization with the types used for 'greater', 'any' and 'equal' is just boilerplate and is stuff that the compiler could figure out by itself, but SV doesn't support this.

We could maybe replace this with:

``EXPECT_CALL(mock, func).with_clazz_mock_func_arg_matchers(greater, (5), any, , equal, (1))

where with_clazz_mock_func_arg_matcher is defined by SVMOCK_FUNCTION(...) and has the arguments:


`define with_clazz_mock_func_arg_matcher( \
    arg1_matcher, arg1_matcher_ctor_args, \
    arg2_matcher, arg2_matcher_ctor_args, \
    arg3_matcher, arg3_matcher_ctor_args)

Inside we can do the instantiation of each arg matcher with its appropriate args. Each matcher has to be a parameterized class of course, so that we can do:

`define with_clazz_mock_func_arg_matcher(
  // ...
  begin
    `arg1_matcher a1m #(int) = new``arg1_matcher_ctor_args; // framework knows that arg1 is 'int'
    `arg2_matcher a2m #(string) = new``arg2_matcher_ctor_args;
    `arg3_matcher a3m #(bit) = new``arg3_matcher_ctor_args;
  end

This is contingent on the fact that it's possible to supply macro arguments that contain brackets (for the constructor args). If I remember correctly it is.

We could go one further and define the following macro:

`define EXPECT_CALL_WITH_ARG_MATCHERS(mock, func, args) \
  ...

that would hide the stuff from above and be called as:

`EXPECT_CALL_WITH_ARG_MATCHERS(mock, func, (greater, (5), any, , equals, (42)))

Notice the extra brackets around the args for the matchers.

@nosnhojn
Copy link
Collaborator

so this is the road I started down yesterday. similar to what you have but so far I think I can avoid the macro and parameterized constructor call...

`EXPECT_CALL(ut, functionIntArgReturnVoid).match_args(int_eq(3));

where int_eq ends up being something like...

function svmock_matcher int_eq(int m);
svmock_int_matcher _m = new(m);
return _m;
endfunction

I suspect I might be on my way to hitting a rookie mistake with a type mismatch somewhere, but I think in the checking we can case(matcher::type) to see which specialization of svmock_matcher is being used and follow to the proper comparison.

@tudortimi
Copy link
Collaborator

I don't think you can case over types like that. Even if you could, why would you want to case? The matcher is supposed to implement the comparison itself. Something similar to JUnit::Matcher:

virtual class matcher #(type T);
  pure virtual function bit is_match(T val);
  pure virtual function void describe_mismatch(T val, description descr);
endclass

The difficulty lies in supplying the matcher with 'val'.

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

No branches or pull requests

3 participants