package org.eclipse.recommenders.completion.rcp.utils;
import static com.google.common.collect.Iterables.getOnlyElement;
import static org.eclipse.recommenders.testing.CodeBuilder.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeThat;
import java.util.Collection;
import java.util.LinkedList;
import org.eclipse.jdt.core.CompletionProposal;
import org.eclipse.recommenders.completion.rcp.IRecommendersCompletionContext;
import org.eclipse.recommenders.testing.rcp.completion.rules.TemporaryProject;
import org.eclipse.recommenders.testing.rcp.completion.rules.TemporaryWorkspace;
import org.eclipse.recommenders.utils.names.IMethodName;
import org.eclipse.recommenders.utils.names.VmMethodName;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import com.google.common.collect.Lists;
@RunWith(Parameterized.class)
public class ProposalUtilsTest {
@ClassRule
public static final TemporaryWorkspace WORKSPACE = new TemporaryWorkspace();
private static final IMethodName METHOD_VOID = VmMethodName.get("LExample.method()V");
private static final IMethodName METHOD_OBJECT = VmMethodName.get("LExample.method(Ljava/lang/Object;)V");
private static final IMethodName METHOD_NUMBER = VmMethodName.get("LExample.method(Ljava/lang/Number;)V");
private static final IMethodName METHOD_COLLECTION = VmMethodName.get("LExample.method(Ljava/util/Collection;)V");
private static final IMethodName SET_INT_STRING = VmMethodName
.get("Ljava/util/List.set(ILjava/lang/Object;)Ljava/lang/Object;");
private static final IMethodName NESTED_METHOD_VOID = VmMethodName.get("LExample$Nested.method()V");
private static final IMethodName INNER_METHOD_VOID = VmMethodName.get("LExample$Inner.method()V");
private static final IMethodName METHOD_INTS = VmMethodName.get("LExample.method([I)V");
private static final IMethodName METHOD_OBJECTS = VmMethodName.get("LExample.method([Ljava/lang/Object;)V");
private static final IMethodName INIT = VmMethodName.get("LExample.<init>()V");
private static final IMethodName INIT_OBJECT = VmMethodName.get("LExample.<init>(Ljava/lang/Object;)V");
private static final IMethodName INIT_NUMBER = VmMethodName.get("LExample.<init>(Ljava/lang/Number;)V");
private static final IMethodName NESTED_INIT = VmMethodName.get("LExample$Nested.<init>()V");
private static final IMethodName NESTED_INIT_OBJECT = VmMethodName
.get("LExample$Nested.<init>(Ljava/lang/Object;)V");
private static final IMethodName INNER_INIT_EXAMPLE = VmMethodName.get("LExample$Inner.<init>(LExample;)V");
private static final IMethodName INNER_INIT_EXAMPLE_OBJECT = VmMethodName
.get("LExample$Inner.<init>(LExample;Ljava/lang/Object;)V");
private static final IMethodName COMPARE_TO_BOOLEAN = VmMethodName
.get("Ljava/lang/Boolean.compareTo(Ljava/lang/Boolean;)I");
private static final IMethodName COMPARABLE_COMPARE_TO_OBJECT = VmMethodName
.get("Ljava/lang/Comparable.compareTo(Ljava/lang/Object;)I");
private static final IMethodName COMPARE_TO_OBJECT = VmMethodName.get("LExample.compareTo(Ljava/lang/Object;)I");
private static final IMethodName COMPARE_TO_EXAMPLE = VmMethodName.get("LExample.compareTo(LExample;)I");
private static final IMethodName OBJECT_HASH_CODE = VmMethodName.get("Ljava/lang/Object.hashCode()I");
private static final IMethodName EXAMPLE_HASH_CODE = VmMethodName.get("LExample.hashCode()I");
private static final IMethodName SUBEXAMPLE_HASH_CODE = VmMethodName.get("LSubExample.hashCode()I");
private static final IMethodName EXAMPLE_CLONE = VmMethodName.get("LExample.clone()Ljava/lang/Object;");
private static final IMethodName OBJECT_CLONE = VmMethodName.get("Ljava/lang/Object.clone()Ljava/lang/Object;");
private final boolean ignore;
private final CharSequence targetTypeCode;
private final CharSequence completionScenarioCode;
private final IMethodName expectedMethod;
public ProposalUtilsTest(boolean ignore, String description, CharSequence targetTypeCode,
CharSequence completionScenarioCode, IMethodName expectedMethod) {
this.ignore = ignore;
this.targetTypeCode = targetTypeCode;
this.completionScenarioCode = completionScenarioCode;
this.expectedMethod = expectedMethod;
}
@Parameters(name = "{index}: {1}")
public static Collection<Object[]> scenarios() {
LinkedList<Object[]> scenarios = Lists.newLinkedList();
// @formatter:off
scenarios.add(scenario("Method with no parameters",
classbody("Example", "public void method() {}"),
method("new Example().method$"),
METHOD_VOID));
scenarios.add(scenario("Method with parameter: Object",
classbody("Example", "public void method(Object o) {}"),
method("new Example().method$"),
METHOD_OBJECT));
scenarios.add(scenario("Method with parameter: int[]",
classbody("Example", "public void method(int[] is) {}"),
method("new Example().method$"),
METHOD_INTS));
scenarios.add(scenario("Method with parameter: Object[]",
classbody("Example", "public void method(Object[] os) {}"),
method("new Example().method$"),
METHOD_OBJECTS));
scenarios.add(scenario("Method with parameter: Collection",
classbody("Example", "public void method(Collection c) {}"),
method("new Example().method$"),
METHOD_COLLECTION));
scenarios.add(scenario("Method with parameter: Collection<Number>",
classbody("Example", "public void method(Collection<Number> c) {}"),
method("new Example().method$"),
METHOD_COLLECTION));
scenarios.add(scenario("Method with parameter: Collection<?>",
classbody("Example", "public void method(Collection<?> c) {}"),
method("new Example().method$"),
METHOD_COLLECTION));
scenarios.add(scenario("Method with parameter: Collection<? extends Number>",
classbody("Example", "public void method(Collection<? extends Number> c) {}"),
method("new Example().method$"),
METHOD_COLLECTION));
scenarios.add(scenario("Method with parameter: Collection<? super Number>",
classbody("Example", "public void method(Collection<? super Number> c) {}"),
method("new Example().method$"),
METHOD_COLLECTION));
scenarios.add(scenario("Method of nested class",
classbody("Example", "public static class Nested { public void method() {} }"),
method("new Example.Nested().method$"),
NESTED_METHOD_VOID));
scenarios.add(scenario("Method of parameterized nested class",
classbody("Example", "public static class Nested<T> { public void method() {} }"),
method("new Example.Nested<String>().method$"),
NESTED_METHOD_VOID));
scenarios.add(scenario("Method of nested class within raw outer class",
classbody("Example<T>", "public static class Nested { public void method() {} }"),
method("new Example.Nested().method$"),
NESTED_METHOD_VOID));
scenarios.add(scenario("Method of inner class",
classbody("Example", "public class Inner { public void method() {} }"),
method("new Example().new Inner().method$"),
INNER_METHOD_VOID));
scenarios.add(scenario("Method of parameterized inner class",
classbody("Example", "public class Inner<T> { public void method() {} }"),
method("new Example().new Inner<String>().method$"),
INNER_METHOD_VOID));
scenarios.add(scenario("Method of inner class within raw outer class",
classbody("Example<T>", "public class Inner { public void method() {} }"),
method("new Example().new Inner().method$"),
INNER_METHOD_VOID));
scenarios.add(scenario("Method of inner class within parameterized outer class",
classbody("Example<T>", "public class Inner { public void method() {} }"),
method("new Example<String>().new Inner().method$"),
INNER_METHOD_VOID));
scenarios.add(postJdt451Scenario("Method of anonymous class",
classbody("Example", "public void method() {}"),
method("Scenario", "new Example() { public void method() { this.method$ } };"),
METHOD_VOID));
scenarios.add(scenario("Overridden method of anonymous class",
classbody("Example", ""),
method("Scenario", "new Example() { public int hashCode() { this.hashCode$ } };"),
EXAMPLE_HASH_CODE));
scenarios.add(postJdt451Scenario("Method of parameterized anonymous class",
classbody("Example<T>", "public void method() {}"),
method("Scenario", "new Example<String>() { public void method() { this.method$ } };"),
METHOD_VOID));
scenarios.add(scenario("Generic method with parameter of raw class",
classbody("Example<T>", "public void method(T t) {}"),
method("new Example().method$"),
METHOD_OBJECT));
scenarios.add(scenario("Generic method with parameter of parameterized class",
classbody("Example<T>", "public void method(T t) {}"),
method("new Example<Number>().method$"),
METHOD_OBJECT));
scenarios.add(scenario("Method With Unspecified Object Bounded Class Parameter As Argument",
classbody("Example<O extends Object>", "public void method(O o) {}"),
method("new Example().method$"),
METHOD_OBJECT));
scenarios.add(postJdt451Scenario("Method With Unspecified Bounded Class Parameter As Argument",
classbody("Example<N extends Number>", "public void method(N n) {}"),
method("new Example().method$"),
METHOD_NUMBER));
scenarios.add(postJdt451Scenario("Method With Specified Bounded Class Parameter As Argument",
classbody("Example<N extends Number>", "public void method(N n) {}"),
method("new Example<Integer>().method$"),
METHOD_NUMBER));
scenarios.add(postJdt451Scenario("Method With Unspecified Multiple Bound Class Parameter As Argument",
classbody("Example<N extends Number & Comparable>", "public void method(N n) {}"),
method("new Example().method$"),
METHOD_NUMBER));
scenarios.add(scenario("Generic method throwing exception parameterized on class",
classbody("Example<T extends Throwable>", "public void method() throws T {}"),
method("new Example().method$"),
METHOD_VOID));
scenarios.add(scenario("Generic method throwing exception parameterized on method",
classbody("Example", "public <T extends Throwable> void method() throws T {}"),
method("new Example().method$"),
METHOD_VOID));
scenarios.add(scenario("Method Call On Unspecified Bounded Class Parameter With Nested Parameterization",
classbody("Example<L extends List<String>>", "public L l;"),
method("new Example().l.set$"),
SET_INT_STRING));
String auxiliaryDefinition = "class Auxiliary<L extends List<String>> { public <N extends L> void method(N n) { } }";
scenarios.add(postJdt451Scenario("Secondary Class With Nested, Bounded Parameters And Method With Bounded Parameter",
classbody("Example", "void method(Auxiliary a) {}") + auxiliaryDefinition,
classbody("SubExample extends Example", "void method(Auxiliary a) { a.method$ }"),
VmMethodName.get("LAuxiliary.method(Ljava/util/List;)V")));
scenarios.add(scenario("Method With Class Parameter Array As Argument",
classbody("Example<T>", "public void method(T[] t) {}"),
method("new Example().method$"),
METHOD_OBJECTS));
scenarios.add(scenario("Method With Unspecified Object Bounded Class Parameter Array As Argument",
classbody("Example<O extends Object>", "public void method(O[] o) {}"),
method("new Example().method$"),
METHOD_OBJECTS));
scenarios.add(scenario("Method With Unspecified Bounded Class Parameter Collection As Argument",
classbody("Example<N extends Number>", "public void method(Collection<N> c) {}"),
method("new Example().method$"),
METHOD_COLLECTION));
scenarios.add(scenario("Generic method with parameter: T",
classbody("Example", "public <T> void method(T t) {}"),
method("new Example().method$"),
METHOD_OBJECT));
scenarios.add(scenario("Generic method with parameter: O extends Object",
classbody("Example", "public <O extends Object> void method(O o) {}"),
method("new Example().method$"),
METHOD_OBJECT));
scenarios.add(postJdt451Scenario("Generic method with parameter: N extends Number",
classbody("Example", "public <N extends Number> void method(N n) {}"),
method("new Example().method$"),
METHOD_NUMBER));
scenarios.add(postJdt451Scenario("Generic method with parameter: N extends Number & Comparable",
classbody("Example", "public <N extends Number & Comparable> void method(N n) {}"),
method("new Example().method$"),
METHOD_NUMBER));
scenarios.add(scenario("Generic method with parameter: T[]",
classbody("Example", "public <T> void method(T[] t) {}"),
method("new Example().method$"),
METHOD_OBJECTS));
scenarios.add(scenario("Generic method with parameter: O[] (O extends Object)",
classbody("Example", "public <O extends Object> void method(O[] o) {}"),
method("new Example().method$"),
METHOD_OBJECTS));
scenarios.add(scenario("Parameterized static method with parameter: T",
classbody("Example", "public static <T> void method(T t) {}"),
method("Example.<Integer>method$"),
METHOD_OBJECT));
scenarios.add(scenario("Parameterized static method with parameter: O extends Object",
classbody("Example", "public static <O extends Object> void method(O o) {}"),
method("Example.<Integer>method$"),
METHOD_OBJECT));
scenarios.add(postJdt451Scenario("Parameterized static method with parameter: N extends Number",
classbody("Example", "public static <N extends Number> void method(N n) {}"),
method("Example.<Integer>method$"),
METHOD_NUMBER));
scenarios.add(postJdt451Scenario("Parameterized static method with parameter: N extends Number & Comparable",
classbody("Example", "public static <N extends Number & Comparable> void method(N n) {}"),
method("Example.<Integer>method$"),
METHOD_NUMBER));
scenarios.add(scenario("Method Call On Object Field Of Class",
classbody("Example", "public Boolean b;"),
method("new Example().b.compareTo$"),
COMPARE_TO_BOOLEAN));
scenarios.add(scenario("Method Call On Interface Field Of Class",
classbody("Example", "public Delayed d;"),
method("new Example().d.compareTo$"),
COMPARABLE_COMPARE_TO_OBJECT));
scenarios.add(scenario("Implicit constructor",
classbody("Example", ""),
classbody("SubExample extends Example", "SubExample() { super($) }"),
INIT));
scenarios.add(scenario("Constructor with no parameters",
classbody("Example", "protected Example() {}"),
classbody("SubExample extends Example", "SubExample() { super($) }"),
INIT));
scenarios.add(scenario("Constructor with parameter: T",
classbody("Example<T>", "protected Example(T t) {}"),
classbody("SubExample extends Example", "SubExample() { super($) }"),
INIT_OBJECT));
scenarios.add(scenario("Constructor with parameter: O extends Object",
classbody("Example<O extends Object>", "protected Example(O o) {}"),
classbody("SubExample extends Example", "SubExample() { super($) }"),
INIT_OBJECT));
scenarios.add(postJdt451Scenario("Constructor with parameter: N extends Number",
classbody("Example<N extends Number>", "protected Example(N n) {}"),
classbody("SubExample extends Example", "SubExample() { super($) }"),
INIT_NUMBER));
scenarios.add(postJdt451Scenario("Constructor with parameter: N extends Number & Comparable",
classbody("Example<N extends Number>", "protected Example(N n) {}"),
classbody("SubExample extends Example", "SubExample() { super($) }"),
INIT_NUMBER));
scenarios.add(scenario("Constructor of nested class",
classbody("Example", "public static class Nested { public Nested() {} }"),
method("new Example.Nested$"),
NESTED_INIT));
scenarios.add(scenario("Constructor of generic nested class",
classbody("Example", "public static class Nested<T> { public Nested(T t) {} }"),
method("new Example.Nested$"),
NESTED_INIT_OBJECT));
scenarios.add(scenario("Constructor of nested class within raw outer class",
classbody("Example<T>", "public static class Nested { public Nested() {} }"),
method("new Example.Nested$"),
NESTED_INIT));
scenarios.add(postJdt451Scenario("Constructor of inner class",
classbody("Example", "public class Inner { public Inner() {} }"),
method("new Example().new Inner$"),
INNER_INIT_EXAMPLE));
scenarios.add(postJdt451Scenario("Constructor of generic inner class",
classbody("Example", "public class Inner<T> { public Inner(T t) {} }"),
method("new Example().new Inner$"),
INNER_INIT_EXAMPLE_OBJECT));
scenarios.add(postJdt451Scenario("Constructor of inner class within raw outer class",
classbody("Example<T>", "public class Inner { public Inner(T t) {} }"),
method("new Example().new Inner$"),
INNER_INIT_EXAMPLE_OBJECT));
scenarios.add(postJdt451Scenario("Constructor of inner class within parameterized outer class",
classbody("Example<T>", "public class Inner { public Inner(T t) {} }"),
method("new Example<String>().new Inner$"),
INNER_INIT_EXAMPLE_OBJECT));
scenarios.add(scenario("Override Superclass Method Inherited From Interface",
classbody("Example implements Comparable", "public int compareTo(Object o) {return 0;} "),
classbody("SubExample extends Example", "compareTo$"),
COMPARE_TO_OBJECT));
scenarios.add(scenario("Override Superclass Method Inherited From Interface, Interface Parameterized By Superclass",
classbody("Example implements Comparable<Example>", "public int compareTo(Example example) {return 0;} "),
classbody("SubExample extends Example", "compareTo$"),
COMPARE_TO_EXAMPLE));
scenarios.add(scenario("Override Superclass Method Inherited From Interface, Interface And Superlass Share Parameter",
classbody("Example<T> implements Comparable<T>", "public int compareTo(Object o) {return 0;} "),
classbody("SubExample extends Example", "compareTo$"),
COMPARE_TO_OBJECT));
scenarios.add(scenario("Override Superclass Method Inherited From Interface, Interface And Class Share Parameter, Class Parameter Bounded",
classbody("Example<N extends Number> implements Comparable<N>", "public int compareTo(Object o) {return 0;}"),
classbody("SubExample extends Example", "compareTo$"),
COMPARE_TO_OBJECT));
scenarios.add(scenario("Overridden Method Of Object Class",
classbody("Example", "public int hashCode() { return 0; }"),
method(" new Example().hashcode$"),
EXAMPLE_HASH_CODE));
scenarios.add(scenario("Non-Overridden Method Of Object Class",
classbody("Example", ""),
classbody("SubExample extends Example", "void method() { this.hashCode$ }"),
OBJECT_HASH_CODE));
scenarios.add(scenario("Method Of Object Class, Overridden by SuperClass",
classbody("Example", "public int hashCode() { return 0; }"),
classbody("SubExample extends Example", "void method() { this.hashCode$ }"),
EXAMPLE_HASH_CODE));
scenarios.add(scenario("Call This. On Object Class Method, Method Overridden By Current Class And SuperClass",
classbody("Example", "public int hashCode() { return 0; }"),
classbody("SubExample extends Example", "public int hashCode() { return 0; } void method() { this.hashCode$ }"),
SUBEXAMPLE_HASH_CODE));
scenarios.add(scenario("Call Super. On Object Class Method, Method Overridden By Current Class And SuperClass",
classbody("Example", "public int hashCode() { return 0; }"),
classbody("SubExample extends Example", "public int hashCode() { return 0; } void method() { super.hashCode$ }"),
EXAMPLE_HASH_CODE));
scenarios.add(scenario("Call Super. On Object Class Method From Anonymous Class, Method Overridden By Anonymous Class And SuperClass",
classbody("Example", "public int hashCode() { return 0; }"),
method("new Example() { public int hashCode() { return 0; } void method() { return super.hashCode$ } };"),
EXAMPLE_HASH_CODE));
scenarios.add(scenario("Overridden Method Of Object Class, Invoked On Array",
classbody("Example", "public int hashCode() { return 0; }"),
method("new Example[0].hashCode$"),
OBJECT_HASH_CODE));
// See <https://bugs.eclipse.org/bugs/show_bug.cgi?id=442723>.
scenarios.add(scenario("Overridden clone method",
classbody("Example", "public Object clone() { return null; }"),
method("new Example().clone$"),
EXAMPLE_CLONE));
scenarios.add(scenario("Clone method of one-dimensional array",
classbody("Example", ""),
method("new Example[0].clone$"),
OBJECT_CLONE));
scenarios.add(scenario("Clone method of two-dimensional array",
classbody("Example", ""),
method("new Example[0][0].clone$"),
OBJECT_CLONE));
// @formatter:on
return scenarios;
}
private static Object[] scenario(String description, CharSequence targetTypeCode,
CharSequence completionScenarioCode, IMethodName expectedMethod) {
return new Object[] { false, description, targetTypeCode, completionScenarioCode, expectedMethod };
}
/**
* A scenario defined using this method will work only with JDT 4.5.1 or greater, which addresses
* <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=467902">Bug 467902</a>.
*/
private static Object[] postJdt451Scenario(String description, CharSequence exampleCU, CharSequence invokingCU,
IMethodName expectedMethod) {
return new Object[] { !ProposalUtils.isGetBindingSupported(), description, exampleCU, invokingCU,
expectedMethod };
}
@Test
public void testSourceBindings() throws Exception {
TemporaryProject dependency = WORKSPACE.createProject();
dependency.createFile(targetTypeCode);
TemporaryProject projectWithSources = WORKSPACE.createProject();
IRecommendersCompletionContext context = projectWithSources.withDependencyOn(dependency)
.createFile(completionScenarioCode).triggerContentAssist();
Collection<CompletionProposal> proposals = context.getProposals().values();
IMethodName actualMethod = ProposalUtils.toMethodName(getOnlyElement(proposals)).get();
// Exercise the SUT even if the assumption fails; this helps catch bugs when the above throws exceptions (which
// it should not).
assumeThat(ignore, is(equalTo(false)));
assertThat(actualMethod, is(equalTo(expectedMethod)));
}
@Test
public void testBinaryBindings() throws Exception {
assumeThat(ignore, is(equalTo(false)));
TemporaryProject dependency = WORKSPACE.createProject();
dependency.createFile(targetTypeCode);
TemporaryProject projectWithSources = WORKSPACE.createProject();
IRecommendersCompletionContext context = projectWithSources.withDependencyOnClassesOf(dependency)
.createFile(completionScenarioCode).triggerContentAssist();
Collection<CompletionProposal> proposals = context.getProposals().values();
IMethodName actualMethod = ProposalUtils.toMethodName(getOnlyElement(proposals)).get();
assertThat(actualMethod, is(equalTo(expectedMethod)));
}
}