-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
Fixes for #637, making theory parameters assignment more consistent across sources #651
Fixes for #637, making theory parameters assignment more consistent across sources #651
Conversation
… assignment time, not parameter use time.
…single-valued methods and fields.
private boolean canAcceptUnboxed(Class<?> candidate) { | ||
Field primitiveClassField = null; | ||
try { | ||
primitiveClassField = candidate.getDeclaredField("TYPE"); |
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.
Generating an exception can be pretty expensive. Can we use a map reversing the one in canAcceptBoxed, instead?
One red flag to me: this might make certain types of theory classes much slower. For example, consider a class BigExpensive that is expensive/slow to create: public class SomeTheories { @test public void foo(int x) { ... } Here, we'll be invoking bigExpensiveOne for foo, which we weren't before. Perhaps we can optimize by detecting when there's no chance that the runtime type of a method return value could possibly match a parameter's type? |
…d be relevant, and theory parameter signatures are compared with types via a prebuilt map rather than reflection.
…lti-valued datapoints
Updated to fix those two comments. We now make the conversion map symmetric, so we don't need to use reflection (so there's no expensive exception thrown), and we check that types are potentially assignable before calling both single-valued and multi-valued datapoint methods. Multi-valued won't be feasible to work like this for iterables because of type erasure, but we can make it work for arrays, as now, so we might as well do that too. |
|
||
public class ParameterSignature { | ||
|
||
@SuppressWarnings("serial") | ||
private static final Map<Class<?>, Class<?>> convertableTypesMap = new HashMap<Class<?>, Class<?>>() {{ |
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 would really prefer that we don't use instance initializers in JUnit. In a constructor like this:
public MyType(...) {
super(a, b);
doSomething(c);
}
It is very surprising that there is code magically executed between these two lines.
Could we instead do:
private static final Map<Class<?>, Class<?>> CONVERTABLE_TYPE_MAP = convertableTypeMap();
As a bonus, 1) convertableTypeMap()
can return an unmodifiable map and 2) you can remove the @SuppressWarnings("serial")
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.
Yes, that's nicer, done.
map.put(double.class, Double.class); | ||
|
||
// Make all type conversions symmetric | ||
Iterable<Entry<Class<?>, Class<?>>> initialEntries = new HashSet<Entry<Class<?>, Class<?>>>(map.entrySet()); |
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.
Perhaps even simpler (and certainly a little more efficient) would be:
addSymmetrically(map, boolean.class, Boolean.class), where
addSymmetrically =
map.put(a, b);
map.put(b, a);
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 don't have any strong feelings on this one way or the other, but I'm happy to change it if you do, so I have.
} | ||
|
||
public Class<?> getType() { | ||
public Class<?> getType() { |
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.
Indentation went weird here?
We're getting close. Can you edit the pull-request summary to summarize the current state of things? Moving forward, I wonder if we could harmonize everything by having the ability for datapoints to return either a Foo or a Provider, with Provider as a way to get deferred computation. |
Summary updated and whitespace fixed. Stream of thought on providers (although this is quite out of scope, and we should open an issue/wiki page/something if you want to take this further): Provider and values is certainly do-able, and committing to that model would nicely clarify the single/multi datapoint method differences. It does sort-of duplicate the ParameterSuppliers though, from the other direction (specifying a source of generated assignments for all parameters in a class, rather than requesting assignments generation from a source for one parameter). What's the difference between a ParameterSupplier and a Provider here? There's clearly some, but it's not really intuitive to me... I think if you want to move to that model and carefully break this backward-compatibility, I'd instead separate by annotations to make it clearer: have I think there's maybe a way you can do this even better and pull it closer into the ParameterSupplier model, so you end up with two clear parallel parameter-source hierarchies -- field/method datapoints for static data values and field/method/class ParameterSuppliers for regenerating data values -- but I haven't totally worked out the model for that yet. Ideas very welcome! |
|
||
public boolean canPotentiallyAcceptType(Class<?> candidate) { | ||
return candidate.isAssignableFrom(type) || | ||
isAssignableViaTypeConversion(candidate, type) || |
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.
Since CONVERTABLE_TYPES_MAP is symmetric, and canAcceptType already checks one direction, we don't have to also check the other direction here, 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.
Never mind, I should have re-loaded the code into my wetware memory before commenting. No change needed here.
Okay, we're in good shape on this pull. I'll merge. Can you make a comment in the release notes? I agree that further discussion would be interesting outside the scope of this pull request, and think your "separate annotation" idea has merit. |
Fixes for #637, making theory parameters assignment more consistent across sources
There's a few inconsistencies in theory implementation, as discussed in #637. This shouldn't make any differences to reasonable normal usage, just to some edge cases.
Essential changes:
(#637 has examples and more details on this)
This conflicts with my other open pull request (#639), but given that we want to make the behaviour in that case more complicated, I think it would be better to fix these inconsistencies first, and then continue that on top. This is also most of the setup work for a fix I have going for #110/#126, as we have to pull types at runtime for iterables there anyway (since the generic type info isn't reliably available).