package org.eclipse.recommenders.subwords.rcp.it; import static java.util.Arrays.asList; import static org.eclipse.recommenders.internal.subwords.rcp.SubwordsSessionProcessor.*; import static org.eclipse.recommenders.utils.Checks.cast; import static org.junit.Assert.fail; import java.util.Collection; import java.util.LinkedList; import java.util.List; import javax.inject.Provider; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor; import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext; import org.eclipse.recommenders.completion.rcp.it.MockedIntelligentCompletionProposalComputer; import org.eclipse.recommenders.completion.rcp.processable.BaseRelevanceSessionProcessor; import org.eclipse.recommenders.completion.rcp.processable.IntelligentCompletionProposalComputer; import org.eclipse.recommenders.completion.rcp.processable.SessionProcessor; import org.eclipse.recommenders.completion.rcp.processable.SessionProcessorDescriptor; import org.eclipse.recommenders.internal.completion.rcp.CompletionRcpPreferences; import org.eclipse.recommenders.internal.subwords.rcp.SubwordsRcpPreferences; import org.eclipse.recommenders.internal.subwords.rcp.SubwordsSessionProcessor; import org.eclipse.recommenders.testing.RetainSystemProperties; import org.eclipse.recommenders.testing.rcp.completion.rules.TemporaryFile; import org.eclipse.recommenders.testing.rcp.completion.rules.TemporaryWorkspace; import org.eclipse.ui.IEditorPart; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.mockito.Mockito; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; @SuppressWarnings("restriction") @RunWith(Parameterized.class) public class SubwordsCompletionProposalComputerIntegrationTest { @ClassRule public static final TemporaryWorkspace WORKSPACE = new TemporaryWorkspace(); @Rule public final RetainSystemProperties retainSystemProperties = new RetainSystemProperties(); private static final int MIN_SUBWORDS_MATCH_RELEVANCE = SUBWORDS_RANGE_START; private static final int MAX_SUBWORDS_MATCH_RELEVANCE = -1; private static final int MIN_CAMELCASE_MATCH_RELEVANCE = 0; private static final int MAX_CAMELCASE_MATCH_RELEVANCE = Integer.MAX_VALUE; private static final int MIN_PREFIX_MATCH_RELEVANCE = 0; private static final int MAX_PREFIX_MATCH_RELEVANCE = Integer.MAX_VALUE; private static final int MIN_EXACT_MATCH_RELEVANCE = CASE_SENSITIVE_EXACT_MATCH_START; private static final int MAX_EXACT_MATCH_RELEVANCE = Integer.MAX_VALUE; private static final int MIN_EXACT_MATCH_IGNORE_CASE_RELEVANCE = CASE_INSENSITIVE_EXACT_MATCH_START; private static final int MAX_EXACT_MATCH_IGNORE_CASE_RELEVANCE = Integer.MAX_VALUE; private static final SubwordsRcpPreferences COMPREHENSIVE = new SubwordsRcpPreferences() { { minPrefixLengthForTypes = 1; } }; private static final SubwordsRcpPreferences PREFIX_LENGTH_5 = new SubwordsRcpPreferences() { { minPrefixLengthForTypes = 5; } }; private final CharSequence code; private final SubwordsRcpPreferences preferences; private final int minRelevance; private final int maxRelevance; private final List<String> expectedProposalPrefixes; public SubwordsCompletionProposalComputerIntegrationTest(String description, CharSequence code, SubwordsRcpPreferences preferences, int minRelevance, int maxRelevance, List<String> expectedProposalPrefixes) { this.code = code; this.preferences = preferences; this.minRelevance = minRelevance; this.maxRelevance = maxRelevance; this.expectedProposalPrefixes = expectedProposalPrefixes; } @Parameters(name = "{index}: {0}") public static Collection<Object[]> scenarios() { LinkedList<Object[]> scenarios = Lists.newLinkedList(); // @formatter:off scenarios.add(scenario("Methods of local variable", "class Example { void m() { Object o = null; o.hc$ } }", COMPREHENSIVE, MIN_SUBWORDS_MATCH_RELEVANCE, MAX_SUBWORDS_MATCH_RELEVANCE, "hashCode")); scenarios.add(scenario("Methods of local variable (upper-case)", "class Example { void m() { Object o = null; o.C$ } }", COMPREHENSIVE, MIN_SUBWORDS_MATCH_RELEVANCE, MAX_SUBWORDS_MATCH_RELEVANCE, "hashCode", "getClass")); scenarios.add(scenario("Methods of local variable's supertype", "class Example { void m() { Example e = null; e.hc$ } }", COMPREHENSIVE, MIN_SUBWORDS_MATCH_RELEVANCE, MAX_SUBWORDS_MATCH_RELEVANCE, "hashCode")); scenarios.add(scenario("Constructors in initialization expression", "class XyzzyExample { void m() { XyzzyExample e = new XyzzyEe$ } }", PREFIX_LENGTH_5, MIN_SUBWORDS_MATCH_RELEVANCE, MAX_SUBWORDS_MATCH_RELEVANCE, "XyzzyExample()")); // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=435745 scenarios.add(scenario("Constructors in standalone expression", "class XyzzyExample { void m() { new XyzzyEe$ } }", PREFIX_LENGTH_5, MIN_SUBWORDS_MATCH_RELEVANCE, MAX_SUBWORDS_MATCH_RELEVANCE, "XyzzyExample()")); // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=435745 scenarios.add(scenario("Return types of method declaration", "class XyzzyExample { XyzzyEe$ m() { } }", PREFIX_LENGTH_5, MIN_SUBWORDS_MATCH_RELEVANCE, MAX_SUBWORDS_MATCH_RELEVANCE, "XyzzyExample")); // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=435745 scenarios.add(scenario("Parameter types of method declaration", "class XyzzyExample { void m(XyzzyEe$) { } }", PREFIX_LENGTH_5, MIN_SUBWORDS_MATCH_RELEVANCE, MAX_SUBWORDS_MATCH_RELEVANCE, "XyzzyExample")); scenarios.add(scenario("Generated getters/setters", "class XyzzyExample { XyzzyExample xyzzyExample; XyzzyEe$ }", PREFIX_LENGTH_5, MIN_SUBWORDS_MATCH_RELEVANCE, MAX_SUBWORDS_MATCH_RELEVANCE, "getXyzzyExample()", "setXyzzyExample(XyzzyExample)")); scenarios.add(scenario("Generated method stub ex", "class Example { ex$ }", COMPREHENSIVE, MIN_EXACT_MATCH_RELEVANCE, MAX_EXACT_MATCH_RELEVANCE, "ex()")); scenarios.add(scenario("Generated method stub exe", "class Example { exe$ }", COMPREHENSIVE, MIN_EXACT_MATCH_RELEVANCE, MAX_EXACT_MATCH_RELEVANCE, "exe()")); scenarios.add(scenario("Subwords Type match", "class ExampleXyzzyFrobz { void m() { Xyzzy$ } }", COMPREHENSIVE, MIN_SUBWORDS_MATCH_RELEVANCE, MAX_SUBWORDS_MATCH_RELEVANCE, "ExampleXyzzyFrobz")); scenarios.add(scenario("Package subwords match", "class Example { void m() { Sys$ } }", COMPREHENSIVE, MIN_SUBWORDS_MATCH_RELEVANCE, MAX_SUBWORDS_MATCH_RELEVANCE, "sun.security.internal.spec")); scenarios.add(scenario("Case sensitive prefix match", "class XyzzyExample { void m() { Xyzzy$ } }", COMPREHENSIVE, MIN_PREFIX_MATCH_RELEVANCE, MAX_PREFIX_MATCH_RELEVANCE, "XyzzyExample")); scenarios.add(scenario("Case insensitive prefix match", "class XyzzyExample { void m() { xyzzy$ } }", COMPREHENSIVE, MIN_PREFIX_MATCH_RELEVANCE, MAX_PREFIX_MATCH_RELEVANCE, "XyzzyExample")); scenarios.add(scenario("Exact match: 5 characters", "class Xyzzy { void method() { Xyzzy$ } }", COMPREHENSIVE, MIN_EXACT_MATCH_RELEVANCE, MAX_EXACT_MATCH_RELEVANCE, "Xyzzy")); scenarios.add(scenario("Exact match: 1 character", "class X { void method() { X$ } }", COMPREHENSIVE, MIN_EXACT_MATCH_RELEVANCE, MAX_EXACT_MATCH_RELEVANCE, "X")); scenarios.add(scenario("Case insensitive exact match: 5 characters", "class Xyzzy { void method() { xyzzy$ } }", COMPREHENSIVE, MIN_EXACT_MATCH_IGNORE_CASE_RELEVANCE, MAX_EXACT_MATCH_IGNORE_CASE_RELEVANCE, "Xyzzy")); scenarios.add(scenario("Case insensitive exact match: 1 character", "class X { void method() { x$ } }", COMPREHENSIVE, MIN_EXACT_MATCH_IGNORE_CASE_RELEVANCE, MAX_EXACT_MATCH_IGNORE_CASE_RELEVANCE, "X")); scenarios.add(scenario("Camel case match", "class XyzzyExample { void m() { XyzzyExample xyzzyExample; xE$ } }", COMPREHENSIVE, MIN_CAMELCASE_MATCH_RELEVANCE, MAX_CAMELCASE_MATCH_RELEVANCE, "xyzzyExample")); scenarios.add(scenario("Exact match of anonymous inner type", "interface XyzzyExample { } class Access { void m() { new XyzzyExample$ } }", PREFIX_LENGTH_5, MIN_SUBWORDS_MATCH_RELEVANCE, MAX_EXACT_MATCH_RELEVANCE, "XyzzyExample()")); // @formatter:on return scenarios; } private static Object[] scenario(String description, CharSequence code, SubwordsRcpPreferences preferences, int minRelevance, int maxRelevance, String... expectedProposals) { return new Object[] { description, code, preferences, minRelevance, maxRelevance, asList(expectedProposals) }; } @Test public void test() throws Exception { List<IJavaCompletionProposal> actualProposals = generateProposals(code, preferences); int lastRelevance = Integer.MAX_VALUE; for (String expectedProposalPrefix : expectedProposalPrefixes) { boolean found = false; for (IJavaCompletionProposal actualProposal : actualProposals) { if (!actualProposal.getDisplayString().startsWith(expectedProposalPrefix)) { continue; } found = true; int relevance = actualProposal.getRelevance(); if (relevance > lastRelevance) { fail(String.format( "Encountered proposal %s with a relevance %d. Expected a relevance lower than the previous expected proposal's relevance of %d.", expectedProposalPrefix, relevance, lastRelevance)); } lastRelevance = relevance; if (relevance < minRelevance || relevance > maxRelevance) { fail(String.format( "Encountered proposal %s with a relevance %d. Expected a relevance between %d and %d", expectedProposalPrefix, relevance, minRelevance, maxRelevance)); } break; } if (!found) { fail(String.format("Expected proposal %s not found", expectedProposalPrefix)); } } } @Before public void setUp() throws CoreException { System.setProperty("org.eclipse.jdt.ui.codeAssistTimeout", "30000"); } @SuppressWarnings({ "rawtypes", "unchecked" }) private List<IJavaCompletionProposal> generateProposals(CharSequence code, SubwordsRcpPreferences preferences) throws CoreException { SessionProcessor processor = new SubwordsSessionProcessor(preferences); SessionProcessor baseRelevanceSessionProcessor = new BaseRelevanceSessionProcessor(); // @formatter:off CompletionRcpPreferences prefs = Mockito.mock(CompletionRcpPreferences.class); Mockito.when(prefs.getEnabledSessionProcessors()).thenReturn(ImmutableSet.of( new SessionProcessorDescriptor("base", "base", "desc", null, 0, true, "", baseRelevanceSessionProcessor), new SessionProcessorDescriptor("subwords", "name", "desc", null, 0, true, "", processor))); // @formatter:on IntelligentCompletionProposalComputer sut = new MockedIntelligentCompletionProposalComputer(processor, prefs, new Provider<IEditorPart>() { @Override public IEditorPart get() { return (IEditorPart) new CompilationUnitEditor(); } }); sut.sessionStarted(); TemporaryFile file = WORKSPACE.createProject().createFile(code); JavaContentAssistInvocationContext ctx = file.triggerContentAssist().getJavaContext(); file.openFileInEditor(); return cast(sut.computeCompletionProposals(ctx, new NullProgressMonitor())); } }