Closed nathanwonnacott closed 4 years ago
There may be a more elegant way to handle this, but there is a simple one line fix that I think will work.
It seems to me that at least in the case where all parameters are strings (and I believe that currently there is only varargs support for methods that contain only strings see issue #12) then as long as the methods with the fewest number of arguments are loaded first, then it will work.
So if you just change AnnotationKeywordExtractor.extractKeywords(Object)
to:
public Map<String, DocumentedKeyword> extractKeywords(final Object keywordBean) {
Map<String, DocumentedKeyword> extractedKeywords = new HashMap<String, DocumentedKeyword>();
Method[] methods = keywordBean.getClass().getMethods();
//Add methods with the most parameters first for funky varargs reasons
//Also, probably come up with a better comment than this
Arrays.sort(methods, (m1, m2) -> -Integer.compare(m1.getParameterCount(), m2.getParameterCount()));
for (final Method method : methods) {
if (method.isAnnotationPresent(RobotKeyword.class) || method.isAnnotationPresent(RobotKeywordOverload.class)) {
createOrAddKeyword(extractedKeywords, keywordBean, method);
}
}
return extractedKeywords;
}
There are some issues with the way AnnotationKeywordExtractor handles overloaded methods when an overload contains a vararg.
The specific example I ran into is from the testfxlibrary.
They have a new keyword defined as:
From my understanding of the
AnnotationKeywordExtractor
code, it will load all of the methods from each keyword class and if it has a@RobotKeyword
or@RobotKeywordOverload
method, it will create aDocumentedKeyword
from them and add them to a map (with the method name as the key). If the name already exists in the map, then it creates an implementation ofDocumentedKeyword
that is a wrapper around the existingDocumentedKeyword
in the map, but first checks the number of arguments, and if it matches its number of arguments, it calls its invoker, otherwise it calls the wrapped invoker.So, assuming that the version of
startApplication
that takes 1 argument was loaded last, you essentially end up with this pseudo-code in yourDocumentedKeyword.execute(Object[] arguments)
method:So if I call the keyword with 4 strings, then it will call the correct
startApplication
method and the argument grouping code will combine the last two strings into an array of strings.However, the keyword methods are loaded by a call to
keywordBean.getClass().getMethods()
(AnnotationKeywordExtractor:32). The documentation fromClass.getMethods
states:If the methods are loaded in a different order, you can end up with the following pseudo code:
Now if you call the keyword with 4 strings, then it will try to run
startApplication(String)
. The argument grouping code will group the 4 strings into an array of strings, attempt to invoke the method that only takes one string and throw anIllegalArgumentException
.The result for me is that my tests work every time on my windows desktop, but they only work about half of the time on my team's Linux Jenkins server.