diff options
| author | Mickael LANOE | 2014-11-20 13:33:56 +0000 |
|---|---|---|
| committer | Mickael LANOE | 2014-11-26 09:48:14 +0000 |
| commit | 2190d4f204539e959b511a5753f54f1d24b83937 (patch) | |
| tree | 9bc910d1579b4dd719b392878bcfea19779e8238 | |
| parent | 8a628e52614bf10e649433e3764a1c78f1d337c8 (diff) | |
| download | org.eclipse.sirius-2190d4f204539e959b511a5753f54f1d24b83937.tar.gz org.eclipse.sirius-2190d4f204539e959b511a5753f54f1d24b83937.tar.xz org.eclipse.sirius-2190d4f204539e959b511a5753f54f1d24b83937.zip | |
[428770] Improve auto-completion for intepreter prefixes
For expression not provided by an existing interpreter, the
auto-completion shows interpreter prefixes that match the expression. So
for empty expression all interpreter prefixes are listed.
Add tests to check this new feature.
Bug: 428770
Change-Id: Ief4033899ae6394f2dec37d571bcd2f22f06d512
Signed-off-by: Mickael LANOE <mickael.lanoe@obeo.fr>
11 files changed, 1446 insertions, 380 deletions
diff --git a/plugins/org.eclipse.sirius.common.ui/src/org/eclipse/sirius/common/ui/tools/internal/contentassist/ContentProposalConverter.java b/plugins/org.eclipse.sirius.common.ui/src/org/eclipse/sirius/common/ui/tools/internal/contentassist/ContentProposalConverter.java index fcbbe1bf34..ad308820bf 100644 --- a/plugins/org.eclipse.sirius.common.ui/src/org/eclipse/sirius/common/ui/tools/internal/contentassist/ContentProposalConverter.java +++ b/plugins/org.eclipse.sirius.common.ui/src/org/eclipse/sirius/common/ui/tools/internal/contentassist/ContentProposalConverter.java @@ -13,7 +13,6 @@ package org.eclipse.sirius.common.ui.tools.internal.contentassist; import java.util.List; import org.eclipse.jface.fieldassist.IContentProposal; - import org.eclipse.sirius.common.tools.api.contentassist.ContentProposal; import org.eclipse.sirius.common.tools.api.util.StringUtil; @@ -58,18 +57,22 @@ public class ContentProposalConverter { } /** - * Make an IContentProposal for the specified String. + * Convert a {@link ContentProposal} to a JFace {@link IContentProposal}. * * @param arg - * @param proposalStart + * proposal to convert. + * @return converted proposal. * */ - private IContentProposal convertToJFaceContentProposal(final ContentProposal arg) { + public IContentProposal convertToJFaceContentProposal(final ContentProposal arg) { String proposal = arg.getProposal(); int cursorPosition = arg.getCursorPosition(); if (!StringUtil.isEmpty(proposalStart) && proposal.startsWith(proposalStart)) { proposal = proposal.substring(proposalStart.length()); - cursorPosition = proposal.length(); + + // cursorPosition is not always at the end of the proposal, so + // just move the position. + cursorPosition -= proposalStart.length(); } return new DefaultContentProposal(proposal, arg.getInformation(), arg.getDisplay(), cursorPosition); diff --git a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/interpreter/CompoundInterpreter.java b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/interpreter/CompoundInterpreter.java index 511ab5eef2..d1f1b996a6 100644 --- a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/interpreter/CompoundInterpreter.java +++ b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/interpreter/CompoundInterpreter.java @@ -27,20 +27,21 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.emf.common.EMFPlugin; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.ECrossReferenceAdapter; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - import org.eclipse.sirius.common.tools.DslCommonPlugin; import org.eclipse.sirius.common.tools.api.contentassist.ContentContext; import org.eclipse.sirius.common.tools.api.contentassist.ContentInstanceContext; import org.eclipse.sirius.common.tools.api.contentassist.ContentProposal; import org.eclipse.sirius.common.tools.api.contentassist.IProposalProvider; +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.common.tools.internal.assist.ContentContextHelper; import org.eclipse.sirius.common.tools.internal.assist.ProposalProviderRegistry; import org.eclipse.sirius.ecore.extender.business.api.accessor.MetamodelDescriptor; import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + /** * Compound interpreter. * @@ -77,7 +78,7 @@ public final class CompoundInterpreter implements IInterpreter, IProposalProvide * the interpreter, they'll be registered here. */ private Collection<MetamodelDescriptor> additionalMetamodels = Sets.newLinkedHashSet(); - + /** The dependencies. */ private final List<String> dependencies; @@ -250,8 +251,9 @@ public final class CompoundInterpreter implements IInterpreter, IProposalProvide while (interpreterProvider == null && entryIterator.hasNext()) { final Map.Entry<IInterpreterProvider, IInterpreter> entry = entryIterator.next(); // instance equality do not suffice as long as the interpreter - // is not a singleton (see CompoundInterpreter.createGenericInterpreter, each session, - // ...) + // is not a singleton (see + // CompoundInterpreter.createGenericInterpreter, each session, + // ...) if (entry.getValue() == interpreter) { interpreterProvider = entry.getKey(); } else if (entry.getValue() != null && entry.getValue().getClass() == interpreter.getClass()) { @@ -784,6 +786,15 @@ public final class CompoundInterpreter implements IInterpreter, IProposalProvide for (IProposalProvider provider : proposalProviders) { proposals.addAll(provider.getProposals(interpreter, context)); } + + if (interpreter == DefaultInterpreterProvider.INSTANCE) { + // The default interpreter is used when there is no other + // interpreter available for the context. Try to find if the + // context matches empty expression from one or several + // interpreters prefixes. + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=428770 + proposals.addAll(getEmptyExpressionProposals(context.getContents())); + } } return proposals; } @@ -833,7 +844,7 @@ public final class CompoundInterpreter implements IInterpreter, IProposalProvide } /** - * Return allways null. use {@link CompoundInterpreter#getVariable(String)} + * Return always null. use {@link CompoundInterpreter#getVariable(String)} * {@inheritDoc} * * @see org.eclipse.sirius.common.tools.api.interpreter.IInterpreter#getVariablePrefix() @@ -910,6 +921,15 @@ public final class CompoundInterpreter implements IInterpreter, IProposalProvide for (IProposalProvider provider : proposalProviders) { proposals.addAll(provider.getProposals(interpreterForExpression, context)); } + + if (interpreterForExpression == DefaultInterpreterProvider.INSTANCE) { + // The default interpreter is used when there is no other + // interpreter available for the context. Try to find if the + // context matches empty expression from one or several + // interpreters prefixes. + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=428770 + proposals.addAll(getEmptyExpressionProposals(context.getTextSoFar())); + } } return proposals; } @@ -958,4 +978,27 @@ public final class CompoundInterpreter implements IInterpreter, IProposalProvide public boolean supportsValidation() { return true; } + + /** + * Get proposals that match context for available empty expressions. + * + * @param context + * context to match + * @return list of proposal for empty expressions + */ + private List<ContentProposal> getEmptyExpressionProposals(String context) { + List<ContentProposal> proposals = Lists.newArrayList(); + + // Provides all interpreters compatible with the context + final List<IProposalProvider> proposalProviders = ProposalProviderRegistry.getAllProviders(); + for (IProposalProvider provider : proposalProviders) { + ContentProposal emptyExpression = provider.getNewEmtpyExpression(); + + if (StringUtil.isEmpty(context) || ContentContextHelper.matchEmptyExpression(context, emptyExpression)) { + proposals.add(emptyExpression); + } + } + + return proposals; + } } diff --git a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/internal/assist/ContentContextHelper.java b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/internal/assist/ContentContextHelper.java index dd07dffc32..98690dc353 100644 --- a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/internal/assist/ContentContextHelper.java +++ b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/internal/assist/ContentContextHelper.java @@ -10,6 +10,10 @@ *******************************************************************************/ package org.eclipse.sirius.common.tools.internal.assist; +import java.util.List; + +import org.eclipse.sirius.common.tools.api.contentassist.ContentProposal; +import org.eclipse.sirius.common.tools.api.interpreter.CompoundInterpreter; import org.eclipse.sirius.common.tools.api.util.StringUtil; /** @@ -50,18 +54,31 @@ public class ContentContextHelper { */ public String getProposalStart() { String proposalStart = ""; - int numberOfalreadyWrittenCharacters = 0; - if (!StringUtil.isEmpty(contents) && position > 0 && position <= contents.length()) { - String substring = contents.substring(0, position); - numberOfalreadyWrittenCharacters = findNumberOfAlreadyWrittenCharacters(substring); - if (numberOfalreadyWrittenCharacters != 0 && numberOfalreadyWrittenCharacters <= substring.length()) { - int propStart = substring.length() - numberOfalreadyWrittenCharacters; - proposalStart = substring.substring(propStart); - - // variable ? - String exprStart = substring.substring(0, propStart); - if (!StringUtil.isEmpty(prefix) && exprStart.endsWith(prefix)) { - proposalStart = prefix + proposalStart; + + // There are two cases considered here. In the first case there is no + // selected interpreter, so the completion is done on interpreters + // empty expressions, we return the entire contents for the proposal + // start. In the second case the proposal start is based on some + // special characters in the contents. + if (!StringUtil.isEmpty(contents)) { + + if (matchEmptyExpressions(contents, CompoundInterpreter.INSTANCE.getAllNewEmtpyExpressions())) { + // First case + proposalStart = contents; + } else if (position > 0 && position <= contents.length()) { + // Second case + int numberOfalreadyWrittenCharacters = 0; + String substring = contents.substring(0, position); + numberOfalreadyWrittenCharacters = findNumberOfAlreadyWrittenCharacters(substring); + if (numberOfalreadyWrittenCharacters != 0 && numberOfalreadyWrittenCharacters <= substring.length()) { + int propStart = substring.length() - numberOfalreadyWrittenCharacters; + proposalStart = substring.substring(propStart); + + // variable ? + String exprStart = substring.substring(0, propStart); + if (!StringUtil.isEmpty(prefix) && exprStart.endsWith(prefix)) { + proposalStart = prefix + proposalStart; + } } } } @@ -99,4 +116,37 @@ public class ContentContextHelper { return numberOfalreadyWrittenCharacters; } + /** + * Return true if the contents matches any interpreters empty expressions. + * + * @param contents + * contents to tests + * @param emptyExpressions + * interpreters empty expressions to match + * @return true if the content matches any interpreters empty expressions + */ + public static boolean matchEmptyExpressions(String contents, List<ContentProposal> emptyExpressions) { + for (ContentProposal emptyExpression : emptyExpressions) { + if (matchEmptyExpression(contents, emptyExpression)) { + return true; + } + } + + return false; + } + + /** + * Return true if the contents matches the interpreter empty expression. + * + * @param contents + * contents to tests + * @param emptyExpression + * one interpreter empty expression to match + * @return true if the content matches the interpreter empty expression + */ + public static boolean matchEmptyExpression(String contents, ContentProposal emptyExpression) { + String proposal = emptyExpression.getProposal(); + return proposal.startsWith(contents) && proposal.length() != contents.length(); + } + } diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/suite/common/AllCommonPluginTests.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/suite/common/AllCommonPluginTests.java index 4bceb7161c..22130948ac 100644 --- a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/suite/common/AllCommonPluginTests.java +++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/suite/common/AllCommonPluginTests.java @@ -61,17 +61,21 @@ import org.eclipse.sirius.tests.unit.common.RefreshEditorsPrecommitListenerTests import org.eclipse.sirius.tests.unit.common.RestoreSessionFromEditorInputTests; import org.eclipse.sirius.tests.unit.common.TransientSessionTests; import org.eclipse.sirius.tests.unit.common.WorkspaceResourceSyncTestCase; +import org.eclipse.sirius.tests.unit.common.interpreter.CompoundInterpreterTestCase; import org.eclipse.sirius.tests.unit.common.interpreter.acceleo.mtl.AcceleoMTInterpreterOnPackageImportTests; import org.eclipse.sirius.tests.unit.common.interpreter.acceleo.mtl.AcceleoMTLCompletionTests; import org.eclipse.sirius.tests.unit.common.interpreter.acceleo.mtl.AcceleoMTLInterpreterTests; import org.eclipse.sirius.tests.unit.common.interpreter.acceleo.mtl.AcceleoPackageRegistryTest; import org.eclipse.sirius.tests.unit.common.interpreter.acceleo.mtl.IInterpreterValidationExpressionTest; import org.eclipse.sirius.tests.unit.common.interpreter.acceleo.mtl.InterpretedExpressionTargetSwitchTest; +import org.eclipse.sirius.tests.unit.common.interpreter.feature.FeatureCompletionTests; import org.eclipse.sirius.tests.unit.common.interpreter.feature.FeatureInterpreterTests; import org.eclipse.sirius.tests.unit.common.interpreter.feature.FeatureProposalProviderTests; import org.eclipse.sirius.tests.unit.common.interpreter.ocl.OCLCompletionTest; +import org.eclipse.sirius.tests.unit.common.interpreter.service.ServiceCompletionTests; import org.eclipse.sirius.tests.unit.common.interpreter.service.ServiceInterpreterTests; import org.eclipse.sirius.tests.unit.common.interpreter.service.ServiceProposalProviderTests; +import org.eclipse.sirius.tests.unit.common.interpreter.variable.VariableCompletionTests; import org.eclipse.sirius.tests.unit.common.interpreter.variable.VariableInterpreterTests; import org.eclipse.sirius.tests.unit.common.interpreter.variable.VariableProposalProviderTests; import org.eclipse.sirius.tests.unit.common.migration.DiagramMigrationTestCampaign01; @@ -238,14 +242,18 @@ public class AllCommonPluginTests extends TestCase { suite.addTestSuite(AcceleoPackageRegistryTest.class); suite.addTestSuite(IInterpreterValidationExpressionTest.class); suite.addTestSuite(FeatureInterpreterTests.class); + suite.addTestSuite(FeatureCompletionTests.class); suite.addTestSuite(ServiceInterpreterTests.class); + suite.addTestSuite(ServiceCompletionTests.class); suite.addTestSuite(VariableInterpreterTests.class); + suite.addTestSuite(VariableCompletionTests.class); suite.addTestSuite(FeatureProposalProviderTests.class); suite.addTestSuite(ServiceProposalProviderTests.class); suite.addTestSuite(VariableProposalProviderTests.class); suite.addTestSuite(InterpretedExpressionTargetSwitchTest.class); suite.addTestSuite(ReloadSessionTest.class); suite.addTestSuite(EclipseUtilTest.class); + suite.addTestSuite(CompoundInterpreterTestCase.class); suite.addTestSuite(TransientSessionTests.class); suite.addTestSuite(RestoreSessionFromEditorInputTests.class); diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/AbstractCompletionTestCase.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/AbstractCompletionTestCase.java new file mode 100644 index 0000000000..e05eb26330 --- /dev/null +++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/AbstractCompletionTestCase.java @@ -0,0 +1,432 @@ +/******************************************************************************* + * Copyright (c) 2014 Obeo. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.tests.unit.common.interpreter; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import junit.framework.TestCase; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EOperation; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.sirius.common.tools.api.contentassist.ContentContext; +import org.eclipse.sirius.common.tools.api.contentassist.ContentInstanceContext; +import org.eclipse.sirius.common.tools.api.contentassist.ContentProposal; +import org.eclipse.sirius.common.tools.api.contentassist.IProposalProvider; +import org.eclipse.sirius.common.tools.api.interpreter.CompoundInterpreter; +import org.eclipse.sirius.common.tools.api.interpreter.DefaultInterpreterContextFactory; +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +/** + * Abstract base class for completion test cases. + * + * @author <a href="mailto:mickael.lanoe@obeo.fr">Mickael LANOE</a> + */ + +public class AbstractCompletionTestCase extends TestCase { + /** + * Concrete interpreter to test. Must be initialized by calling + * {@link AbstractCompletionTestCase#setInterpreterAndProposalProvider()} in + * a method that overrides {@link TestCase#setUp()} + */ + protected IInterpreter concreteInterpreter; + + /** + * Concrete proposal provider to test. Must be initialized by calling + * {@link AbstractCompletionTestCase#setInterpreterAndProposalProvider()} in + * a method that overrides {@link TestCase#setUp()} + */ + protected IProposalProvider concreteProposalProvider; + + /** + * A proposal function for the current provider that takes a + * {@link ContentContext} + */ + protected Function<ContentContext, List<ContentProposal>> proposalFunction = new Function<ContentContext, List<ContentProposal>>() { + public List<ContentProposal> apply(ContentContext input) { + return getProposals(input); + } + }; + + /** + * A proposal function for the current provider that takes a + * {@link ContentInstanceContext} + */ + protected Function<ContentInstanceContext, List<ContentProposal>> proposalInstanceFunction = new Function<ContentInstanceContext, List<ContentProposal>>() { + public List<ContentProposal> apply(ContentInstanceContext input) { + return getProposals(input); + } + }; + + /** + * A proposal function for {@link CompoundInterpreter} that takes a + * {@link ContentContext} + */ + protected Function<ContentContext, List<ContentProposal>> compoundProposalFunction = new Function<ContentContext, List<ContentProposal>>() { + public List<ContentProposal> apply(ContentContext input) { + return CompoundInterpreter.INSTANCE.getProposals(CompoundInterpreter.INSTANCE, input); + } + }; + + /** + * A proposal function for {@link CompoundInterpreter} that takes a + * {@link ContentInstanceContext} + */ + protected Function<ContentInstanceContext, List<ContentProposal>> compoundProposalInstanceFunction = new Function<ContentInstanceContext, List<ContentProposal>>() { + public List<ContentProposal> apply(ContentInstanceContext input) { + return CompoundInterpreter.INSTANCE.getProposals(CompoundInterpreter.INSTANCE, input); + } + }; + + @Override + protected void tearDown() throws Exception { + if (concreteInterpreter != null) { + concreteInterpreter.dispose(); + } + concreteInterpreter = null; + concreteProposalProvider = null; + super.tearDown(); + } + + /** + * Set the interpreter and the proposal provider. + * + * @param interpreter + * the interpreter to use in the test + * @param proposalProvider + * the proposal provider to use in the test + */ + protected void setInterpreterAndProposalProvider(IInterpreter interpreter, IProposalProvider proposalProvider) { + this.concreteInterpreter = interpreter; + this.concreteProposalProvider = proposalProvider; + } + + /** + * Evaluates the content proposals for a given expression and returns the + * result as a list. + * + * @param context + * the context + * @return content proposal list + */ + protected List<ContentProposal> getProposals(ContentContext context) { + return concreteProposalProvider.getProposals(concreteInterpreter, context); + } + + /** + * Evaluates the content proposals for a given expression and returns the + * result as a list. + * + * @param context + * the context + * @return content proposal list + */ + protected List<ContentProposal> getProposals(ContentInstanceContext context) { + return concreteProposalProvider.getProposals(concreteInterpreter, context); + } + + /** + * Create a {@link ContentContext}. + * + * @param expression + * expression of the context + * @param cursorPosition + * cursor position in the expression + * @param eClass + * target domain class + * @return a new {@link ContentContext} + */ + protected ContentContext createContentContext(String expression, int cursorPosition, String eClass) { + return createContentContext(expression, cursorPosition, eClass, Collections.<String, String> emptyMap()); + } + + /** + * Create a {@link ContentContext}. + * + * @param expression + * expression of the context + * @param cursorPosition + * cursor position in the expression + * @param element + * the concerned element + * @param eClass + * target domain class + * @return a new {@link ContentContext} + */ + protected ContentContext createContentContext(String expression, int cursorPosition, EObject element, String eClass) { + return createContentContext(expression, cursorPosition, element, eClass, null, Collections.<String, String> emptyMap(), Collections.<String> emptyList()); + } + + /** + * Create a {@link ContentContext}. + * + * @param expression + * expression of the context + * @param cursorPosition + * cursor position in the expression + * @param eClass + * target domain class + * @param variables + * declared variables + * @return a new {@link ContentContext} + */ + protected ContentContext createContentContext(String expression, int cursorPosition, String eClass, Map<String, String> variables) { + return createContentContext(expression, cursorPosition, eClass, null, variables, Collections.<String> emptyList()); + } + + /** + * Create a {@link ContentContext}. + * + * @param expression + * expression of the context + * @param cursorPosition + * cursor position in the expression + * @param eClass + * target domain class + * @param ePackage + * available EPackage + * @return a new {@link ContentContext} + */ + protected ContentContext createContentContext(String expression, int cursorPosition, String eClass, EPackage ePackage) { + return createContentContext(expression, cursorPosition, eClass, ePackage, Collections.<String, String> emptyMap(), Collections.<String> emptyList()); + } + + /** + * Create a {@link ContentContext}. + * + * @param expression + * expression of the context + * @param cursorPosition + * cursor position in the expression + * @param element + * the concerned element + * @param eClass + * target domain class + * @param ePackage + * available EPackage + * @return a new {@link ContentContext} + */ + protected ContentContext createContentContext(String expression, int cursorPosition, EObject element, String eClass, EPackage ePackage) { + return createContentContext(expression, cursorPosition, element, eClass, ePackage, Collections.<String, String> emptyMap(), Collections.<String> emptyList()); + } + + /** + * Create a {@link ContentContext}. + * + * @param expression + * expression of the context + * @param cursorPosition + * cursor position in the expression + * @param eClass + * target domain class + * @param ePackage + * available EPackage + * @param variables + * the defined variables + * @param dependencies + * the list of available dependencies + * @return a new {@link ContentContext} + */ + protected ContentContext createContentContext(String expression, int cursorPosition, String eClass, EPackage ePackage, Map<String, String> variables, Collection<String> dependencies) { + return createContentContext(expression, cursorPosition, null, eClass, ePackage, variables, dependencies); + } + + /** + * Create a {@link ContentContext}. + * + * @param expression + * expression of the context + * @param cursorPosition + * cursor position in the expression + * @param element + * the concerned element + * @param eClass + * target domain class + * @param ePackage + * available EPackage + * @param variables + * the defined variables + * @param dependencies + * the list of available dependencies + * @return a new {@link ContentContext} + */ + protected ContentContext createContentContext(String expression, int cursorPosition, EObject element, String eClass, EPackage ePackage, Map<String, String> variables, + Collection<String> dependencies) { + Collection<EPackage> pList = ePackage == null ? Collections.<EPackage> emptyList() : Collections.singletonList(ePackage); + return new ContentContext(expression, cursorPosition, DefaultInterpreterContextFactory.createInterpreterContext(element, true, null, Collections.singletonList(eClass), pList, variables, + dependencies)); + } + + /** + * Get proposals from content proposals. + * + * @param proposals + * content proposals + * @return collection of proposal string + */ + protected Collection<String> extractProposal(List<ContentProposal> proposals) { + return Lists.newArrayList(Iterables.transform(proposals, new Function<ContentProposal, String>() { + public String apply(ContentProposal from) { + return from.getProposal(); + } + })); + } + + /** + * Ensure that proposal collection contains expected proposals. + * + * @param expectedProposals + * expected proposals + * @param proposals + * proposals to check + * @param concerned + * predicate to filter expected proposals + * @return error message or empty string + */ + protected StringBuilder lookForExpectedProposals(Iterable<String> expectedProposals, Collection<String> proposals, Predicate<String> concerned) { + StringBuilder tmpMsg = new StringBuilder(); + + for (String prop : Iterables.filter(expectedProposals, concerned)) { + if (proposals.contains(prop)) { + proposals.remove(prop); + } else { + tmpMsg.append("\n * " + prop); + } + } + + return tmpMsg; + } + + /** + * Ensures that the given proposal lists contain all the given expected + * variables. + * + * @param variables + * the expected variables + * @param proposals + * the proposals computed by the OCL interpreter + * @param concerned + * a predicated allowing to select only the elements starting + * with the expected prefix + * @param errorMsg + * the errorMsg to use for listing missing proposals + */ + protected void checkVariables(Set<String> expectedVariables, Collection<String> proposals, Predicate<String> concerned, StringBuilder errorMsg) { + // Variables + StringBuilder tmpMsg = lookForExpectedProposals(expectedVariables, proposals, concerned); + + if (tmpMsg.length() != 0) { + tmpMsg.insert(0, "\nSome expected variables are not present in completion proposals:"); + errorMsg.append(tmpMsg.toString()); + } + } + + /** + * Ensures that the given proposal lists contain all the eStructuralFeatures + * of the given {@link EClass} starting with the expected prefix. + * + * @param eClass + * the EClass on which completion is called + * @param proposals + * the proposals computed by the OCL interpreter + * @param concerned + * a predicated allowing to select only the elements starting + * with the expected prefix + * @param errorMsg + * the errorMsg to use for listing missing proposals + */ + protected void checkEStruturalFeatures(EClass eClass, Collection<String> proposals, Predicate<String> concerned, StringBuilder errorMsg) { + Function<EStructuralFeature, String> getExpectedProposal = new Function<EStructuralFeature, String>() { + public String apply(EStructuralFeature from) { + return from.getName(); + } + }; + + // EStructuralfeatures + StringBuilder tmpMsg = lookForExpectedProposals(Iterables.transform(eClass.getEAllStructuralFeatures(), getExpectedProposal), proposals, concerned); + + if (tmpMsg.length() != 0) { + tmpMsg.insert(0, "\nSome expected features are not present in completion proposals:"); + errorMsg.append(tmpMsg.toString()); + } + } + + /** + * Ensures that the given proposal lists contain all the eOperations of the + * given {@link EClass} starting with the expected prefix. + * + * @param eClass + * the EClass on which completion is called + * @param implicitContext + * implicit context + * @param proposals + * the proposals computed by the OCL interpreter + * @param concerned + * a predicated allowing to select only the elements starting + * with the expected prefix + * @param signature + * function class to get operation signature + * @param errorMsg + * the errorMsg to use for listing missing proposals + */ + protected void checkEOperations(EClass eClass, boolean implicitContext, Collection<String> proposals, Predicate<String> concerned, Function<EOperation, String> signature, StringBuilder errorMsg) { + // EOperations + Collection<EOperation> opToCheck = Lists.newArrayList(); + Collection<String> opNames = Sets.newHashSet(); + for (EOperation op : eClass.getEAllOperations()) { + if (!(implicitContext && opNames.contains(op.getName()))) { + opNames.add(op.getName()); + opToCheck.add(op); + } + } + + StringBuilder tmpMsg = lookForExpectedProposals(Iterables.transform(opToCheck, signature), proposals, concerned); + + if (tmpMsg.length() != 0) { + tmpMsg.insert(0, "\nSome expected operations are not present in completion proposals:"); + errorMsg.append(tmpMsg.toString()); + } + } + + /** + * Ensure that the completion matches the interpreter empty expression + * + * @param context + * context to used + * @param getProposalFunction + * function class to call to have proposals + */ + protected <T> void assertCompletionMatchesEmptyExpression(T context, Function<T, List<ContentProposal>> getProposalFunction) { + List<ContentProposal> proposals = getProposalFunction.apply(context); + final ContentProposal emptyExpressionProposal = concreteProposalProvider.getNewEmtpyExpression(); + + assertEquals(1, proposals.size()); + ContentProposal proposal = proposals.get(0); + + // ContentProposal.equals() does not test the display and the cursor + // position. + assertEquals(emptyExpressionProposal.getProposal(), proposal.getProposal()); + assertEquals(emptyExpressionProposal.getDisplay(), proposal.getDisplay()); + assertEquals(emptyExpressionProposal.getCursorPosition(), proposal.getCursorPosition()); + } +} diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/CompoundInterpreterTestCase.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/CompoundInterpreterTestCase.java new file mode 100644 index 0000000000..8332cb977a --- /dev/null +++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/CompoundInterpreterTestCase.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright (c) 2014 Obeo. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.tests.unit.common.interpreter; + +import java.util.List; + +import junit.framework.TestCase; + +import org.eclipse.jface.fieldassist.IContentProposal; +import org.eclipse.sirius.common.tools.api.contentassist.ContentContext; +import org.eclipse.sirius.common.tools.api.contentassist.ContentProposal; +import org.eclipse.sirius.common.tools.api.interpreter.CompoundInterpreter; +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreterContext; +import org.eclipse.sirius.common.ui.tools.internal.contentassist.ContentProposalConverter; +import org.eclipse.sirius.diagram.description.DescriptionFactory; +import org.eclipse.sirius.diagram.description.DescriptionPackage; +import org.eclipse.sirius.diagram.description.DiagramDescription; +import org.eclipse.sirius.tools.api.interpreter.context.SiriusInterpreterContextFactory; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +/** + * Tests for {@link CompoundInterpreter} + * + * @author <a href="mailto:mickael.lanoe@obeo.fr">Mickael LANOE</a> + */ +public class CompoundInterpreterTestCase extends TestCase { + /** + * available interpreters empty expressions + */ + private List<ContentProposal> availableEmptyExpressions; + + /** + * Interpreter context + */ + private IInterpreterContext interpreterContext; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + // Get available interpreters empty expressions + availableEmptyExpressions = CompoundInterpreter.INSTANCE.getAllNewEmtpyExpressions(); + assertFalse("At least one interpreter should be declared", availableEmptyExpressions.isEmpty()); + + // Get an interpreter context + DiagramDescription diagramDescription = DescriptionFactory.eINSTANCE.createDiagramDescription(); + interpreterContext = SiriusInterpreterContextFactory.createInterpreterContext(diagramDescription, DescriptionPackage.Literals.DIAGRAM_DESCRIPTION__PRECONDITION_EXPRESSION); + } + + @Override + protected void tearDown() throws Exception { + availableEmptyExpressions = null; + interpreterContext = null; + + super.tearDown(); + } + + /** + * Ensures that we get all available interpreters prefixes for an empty or + * null expression. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=428770 + */ + public void testCompletionForEmptyExpression() { + List<ContentProposal> proposalsForNullExpression = getProposals(null); + assertTrue("Proposals should contains all interpreters empty expressions for a null context", + proposalsForNullExpression.containsAll(availableEmptyExpressions) && proposalsForNullExpression.size() == availableEmptyExpressions.size()); + + List<ContentProposal> proposalsForEmptyExpression = getProposals(""); + assertTrue("Proposals should contains all interpreters empty expressions for an empty context", proposalsForEmptyExpression.containsAll(availableEmptyExpressions) + && proposalsForEmptyExpression.size() == availableEmptyExpressions.size()); + } + + /** + * Ensures that we get interpreters empty expressions that match a non empty + * context. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=428770 + */ + public void testCompletionPerPrefixFirstChar() { + for (ContentProposal emptyExpression : availableEmptyExpressions) { + String firstChar = emptyExpression.getProposal().substring(0, 1); + List<ContentProposal> proposals = getProposals(firstChar); + ContentProposalConverter converter = new ContentProposalConverter(firstChar); + + // The following assert is valid for: "[/]" "<%%>" "ocl:" "var:" + // "service:" and "feature" + assertEquals("Proposals should contains only one proposal", 1, proposals.size()); + + // Proposals are interpreter prefixes + assertTrue("Proposals should be contained by available empty expressions", availableEmptyExpressions.containsAll(proposals)); + + ContentProposal proposal = proposals.get(0); + IContentProposal jfaceProposal = converter.convertToJFaceContentProposal(proposal); + + // Check JFace proposal + assertEquals("", proposal.getProposal(), firstChar + jfaceProposal.getContent()); + + // Check JFace cursor position + assertEquals("JFace proposal cursor position is not well computed", proposal.getCursorPosition() - (proposal.getProposal().length() - jfaceProposal.getContent().length()), + jfaceProposal.getCursorPosition()); + } + } + + /** + * Ensures that for a valid interpreter syntax, we get proposals for the + * selected interpreter but not for interpreters empty expressions. See + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=428770 + */ + public void testCompletionForInterpreterEmptyExpression() { + for (ContentProposal emptyExpression : availableEmptyExpressions) { + final List<ContentProposal> proposals = getProposals(emptyExpression.getProposal(), emptyExpression.getCursorPosition()); + + // Proposals must be part of the selected interpreter, so not in + // available empty expressions + assertFalse("Proposals must not contains interpreter empty expressions", Iterables.any(availableEmptyExpressions, new Predicate<ContentProposal>() { + @Override + public boolean apply(ContentProposal arg0) { + return proposals.contains(arg0); + } + })); + } + } + + /** + * Get proposals for a given expression + * + * @param expression + * given expression + * @return proposals + */ + private List<ContentProposal> getProposals(String expression) { + return getProposals(expression, expression != null ? expression.length() : 0); + } + + /** + * Get proposals for a given expression + * + * @param expression + * given expression + * @param cursorPosition + * cursor position + * @return proposals + */ + private List<ContentProposal> getProposals(String expression, int cursorPosition) { + ContentContext context = new ContentContext(expression, cursorPosition, interpreterContext); + return CompoundInterpreter.INSTANCE.getProposals(CompoundInterpreter.INSTANCE, context); + } + +} diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/acceleo/mtl/AcceleoMTLCompletionTests.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/acceleo/mtl/AcceleoMTLCompletionTests.java index 40b0860243..0724f0ab7e 100644 --- a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/acceleo/mtl/AcceleoMTLCompletionTests.java +++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/acceleo/mtl/AcceleoMTLCompletionTests.java @@ -14,15 +14,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Set; -import junit.framework.TestCase; - import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EOperation; -import org.eclipse.emf.ecore.EPackage; -import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EcoreFactory; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.sirius.common.acceleo.mtl.business.internal.interpreter.AcceleoMTLInterpreter; @@ -31,47 +26,26 @@ import org.eclipse.sirius.common.acceleo.mtl.ide.AcceleoProposalProvider; import org.eclipse.sirius.common.tools.api.contentassist.ContentContext; import org.eclipse.sirius.common.tools.api.contentassist.ContentInstanceContext; import org.eclipse.sirius.common.tools.api.contentassist.ContentProposal; -import org.eclipse.sirius.common.tools.api.contentassist.IProposalProvider; -import org.eclipse.sirius.common.tools.api.interpreter.DefaultInterpreterContextFactory; import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; import org.eclipse.sirius.common.tools.api.util.StringUtil; import org.eclipse.sirius.diagram.DNode; import org.eclipse.sirius.diagram.DiagramFactory; import org.eclipse.sirius.diagram.DiagramPackage; +import org.eclipse.sirius.tests.SiriusTestsPlugin; +import org.eclipse.sirius.tests.unit.common.interpreter.AbstractCompletionTestCase; import com.google.common.base.Function; import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import org.eclipse.sirius.tests.SiriusTestsPlugin; - /** * Tests for the {@link AcceleoMTLInterpreter} utility class. * * @author mporhel * */ -public class AcceleoMTLCompletionTests extends TestCase { - - private IInterpreter interpreter; - - private IProposalProvider proposalProvider; - - private Function<ContentContext, List<ContentProposal>> proposalFunction = new Function<ContentContext, List<ContentProposal>>() { - public List<ContentProposal> apply(ContentContext input) { - IProposalProvider proposalProvider = new AcceleoProposalProvider(); - return proposalProvider.getProposals(interpreter, input); - } - }; - - private Function<ContentInstanceContext, List<ContentProposal>> proposalInstanceFunction = new Function<ContentInstanceContext, List<ContentProposal>>() { - public List<ContentProposal> apply(ContentInstanceContext input) { - IProposalProvider proposalProvider = new AcceleoProposalProvider(); - return proposalProvider.getProposals(interpreter, input); - } - }; +public class AcceleoMTLCompletionTests extends AbstractCompletionTestCase { private static final String IMPORT = "org::eclipse::sirius::tests::unit::common::interpreter::acceleo::mtl::AcceleoMtlInterpreterTestModule"; @@ -83,21 +57,36 @@ public class AcceleoMTLCompletionTests extends TestCase { } }; + @Override protected void setUp() throws Exception { super.setUp(); - - interpreter = new AcceleoMTLInterpreter(); - proposalProvider = new AcceleoProposalProvider(); + setInterpreterAndProposalProvider(new AcceleoMTLInterpreter(), new AcceleoProposalProvider()); }; - @Override - protected void tearDown() throws Exception { - interpreter.dispose(); - interpreter = null; + /** + * Tests proposal based on acceleo prefix. + */ + public void testAcceleoMTLCompletionInterpreterPrefix() { - proposalProvider = null; + ContentContext cc = createContentContext("[", 1, "EClass"); + assertCompletionMatchesEmptyExpression(cc, compoundProposalFunction); - super.tearDown(); + cc = createContentContext("[/", 2, "EClass"); + assertCompletionMatchesEmptyExpression(cc, compoundProposalFunction); + } + + /** + * Tests proposal based on acceleo prefix. + */ + public void testAcceleoMTLInstanceCompletionInterpreterPrefix() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + c.setName("c1"); + + ContentInstanceContext cic = new ContentInstanceContext(c, "[", 1); + assertCompletionMatchesEmptyExpression(cic, compoundProposalInstanceFunction); + + cic = new ContentInstanceContext(c, "[/", 2); + assertCompletionMatchesEmptyExpression(cic, compoundProposalInstanceFunction); } /** @@ -108,12 +97,10 @@ public class AcceleoMTLCompletionTests extends TestCase { eClass.setName("c"); ContentContext cc = null; - assertTrue(proposalProvider.getProposals(interpreter, cc).isEmpty()); + assertTrue(getProposals(cc).isEmpty()); ContentInstanceContext cic = null; - assertTrue(proposalProvider.getProposals(interpreter, cic).isEmpty()); - - interpreter.dispose(); + assertTrue(getProposals(cic).isEmpty()); } /** @@ -121,8 +108,7 @@ public class AcceleoMTLCompletionTests extends TestCase { */ public void testAcceleoMTLCompletionEmtpyField() { ContentContext cc = createContentContext("", 0, "EClass"); - - doTestAcceleoMTLCompletionEmtpyField(cc, proposalFunction); + assertCompletionMatchesEmptyExpression(cc, proposalFunction); } /** @@ -133,22 +119,7 @@ public class AcceleoMTLCompletionTests extends TestCase { c.setName("c1"); ContentInstanceContext cic = new ContentInstanceContext(c, "", 0); - - doTestAcceleoMTLCompletionEmtpyField(cic, proposalInstanceFunction); - } - - private <T> void doTestAcceleoMTLCompletionEmtpyField(T context, Function<T, List<ContentProposal>> getProposalFunction) { - List<ContentProposal> proposals = getProposalFunction.apply(context); - - assertEquals(1, proposals.size()); - assertEquals("[/]", proposals.get(0).getProposal()); - assertEquals("[/]", proposals.get(0).getDisplay()); - assertEquals(1, proposals.get(0).getCursorPosition()); - - ContentProposal newEmtpyExpression = proposalProvider.getNewEmtpyExpression(); - assertEquals("[/]", newEmtpyExpression.getProposal()); - assertEquals("[/]", newEmtpyExpression.getDisplay()); - assertEquals(1, newEmtpyExpression.getCursorPosition()); + assertCompletionMatchesEmptyExpression(cic, proposalInstanceFunction); } /** @@ -238,15 +209,15 @@ public class AcceleoMTLCompletionTests extends TestCase { ContentContext cc = createContentContext("[/]", 0, "EClass"); // No completion before [ - List<ContentProposal> contentProposals = proposalProvider.getProposals(interpreter, cc); + List<ContentProposal> contentProposals = getProposals(cc); // assertTrue(contentProposals.isEmpty()); // implicit context cc = createContentContext("[/]", 1, "EClass"); - contentProposals = proposalProvider.getProposals(interpreter, cc); + contentProposals = getProposals(cc); Set<String> vars = Sets.newHashSet(); - vars.addAll(interpreter.getVariables().keySet()); + vars.addAll(concreteInterpreter.getVariables().keySet()); vars.add("thisEObject"); vars.add("self"); @@ -254,15 +225,15 @@ public class AcceleoMTLCompletionTests extends TestCase { // cc = createContentContext("[self./]", 6, "EClass"); - contentProposals = proposalProvider.getProposals(interpreter, cc); + contentProposals = getProposals(cc); - checkCompletionProposal(c.eClass(), contentProposals, interpreter.getVariables().keySet(), false); + checkCompletionProposal(c.eClass(), contentProposals, concreteInterpreter.getVariables().keySet(), false); // qualified name cc = createContentContext("[self./]", 6, "ecore.EClass"); - contentProposals = proposalProvider.getProposals(interpreter, cc); + contentProposals = getProposals(cc); - checkCompletionProposal(c.eClass(), contentProposals, interpreter.getVariables().keySet(), false); + checkCompletionProposal(c.eClass(), contentProposals, concreteInterpreter.getVariables().keySet(), false); } /** @@ -276,14 +247,14 @@ public class AcceleoMTLCompletionTests extends TestCase { ContentInstanceContext cic = new ContentInstanceContext(c, "[/]", 0); // No completion before [ - List<ContentProposal> contentProposals = proposalProvider.getProposals(interpreter, cic); + List<ContentProposal> contentProposals = getProposals(cic); // assertTrue(contentProposals.isEmpty()); cic = new ContentInstanceContext(c, "[/]", 1); - contentProposals = proposalProvider.getProposals(interpreter, cic); + contentProposals = getProposals(cic); Set<String> vars = Sets.newHashSet(); - vars.addAll(interpreter.getVariables().keySet()); + vars.addAll(concreteInterpreter.getVariables().keySet()); vars.add("thisEObject"); vars.add("self"); @@ -291,8 +262,8 @@ public class AcceleoMTLCompletionTests extends TestCase { // cic = new ContentInstanceContext(c, "[self./]", 6); - contentProposals = proposalProvider.getProposals(interpreter, cic); - checkCompletionProposal(c.eClass(), contentProposals, interpreter.getVariables().keySet(), false); + contentProposals = getProposals(cic); + checkCompletionProposal(c.eClass(), contentProposals, concreteInterpreter.getVariables().keySet(), false); } /** @@ -307,11 +278,11 @@ public class AcceleoMTLCompletionTests extends TestCase { ContentContext cc = createContentContext("[/]", 0, "DNode", DiagramPackage.eINSTANCE, Collections.<String, String> emptyMap(), Collections.<String> emptyList()); // No completion before [ - List<ContentProposal> contentProposals = proposalProvider.getProposals(interpreter, cc); + List<ContentProposal> contentProposals = getProposals(cc); // assertTrue(contentProposals.isEmpty()); cc = createContentContext("[/]", 1, "DNode", DiagramPackage.eINSTANCE, Collections.<String, String> emptyMap(), Collections.<String> emptyList()); - contentProposals = proposalProvider.getProposals(interpreter, cc); + contentProposals = getProposals(cc); Set<String> vars = Sets.newHashSet(); vars.add("thisEObject"); @@ -320,14 +291,14 @@ public class AcceleoMTLCompletionTests extends TestCase { checkCompletionProposal(dNode.eClass(), contentProposals, vars, true); cc = createContentContext("[self./]", 6, "DNode", DiagramPackage.eINSTANCE, Collections.<String, String> emptyMap(), Collections.<String> emptyList()); - contentProposals = proposalProvider.getProposals(interpreter, cc); + contentProposals = getProposals(cc); - checkCompletionProposal(dNode.eClass(), contentProposals, interpreter.getVariables().keySet(), false); + checkCompletionProposal(dNode.eClass(), contentProposals, concreteInterpreter.getVariables().keySet(), false); cc = createContentContext("[self./]", 6, "diagram.DNode", DiagramPackage.eINSTANCE, Collections.<String, String> emptyMap(), Collections.<String> emptyList()); - contentProposals = proposalProvider.getProposals(interpreter, cc); + contentProposals = getProposals(cc); - checkCompletionProposal(dNode.eClass(), contentProposals, interpreter.getVariables().keySet(), false); + checkCompletionProposal(dNode.eClass(), contentProposals, concreteInterpreter.getVariables().keySet(), false); } /** @@ -341,14 +312,14 @@ public class AcceleoMTLCompletionTests extends TestCase { ContentInstanceContext cic = new ContentInstanceContext(dNode, "[/]", 0); // No completion before [ - List<ContentProposal> contentProposals = proposalProvider.getProposals(interpreter, cic); + List<ContentProposal> contentProposals = getProposals(cic); // assertTrue(contentProposals.isEmpty()); cic = new ContentInstanceContext(dNode, "[/]", 1); - contentProposals = proposalProvider.getProposals(interpreter, cic); + contentProposals = getProposals(cic); Set<String> vars = Sets.newHashSet(); - vars.addAll(interpreter.getVariables().keySet()); + vars.addAll(concreteInterpreter.getVariables().keySet()); vars.add("thisEObject"); vars.add("self"); @@ -356,9 +327,9 @@ public class AcceleoMTLCompletionTests extends TestCase { // cic = new ContentInstanceContext(dNode, "[self./]", 6); - contentProposals = proposalProvider.getProposals(interpreter, cic); + contentProposals = getProposals(cic); - checkCompletionProposal(dNode.eClass(), contentProposals, interpreter.getVariables().keySet(), false); + checkCompletionProposal(dNode.eClass(), contentProposals, concreteInterpreter.getVariables().keySet(), false); } /** @@ -400,11 +371,11 @@ public class AcceleoMTLCompletionTests extends TestCase { * "[self.name.concat()/]", cursor : 18 */ private <T> void doTestAcceleoMTLCompletionWithVariables(EClass c, String varName, T context1, T context2, Function<T, List<ContentProposal>> getProposalFunction) { - interpreter.setVariable(varName, c); - assertEquals("Variables was not declared", 1, interpreter.getVariables().size()); + concreteInterpreter.setVariable(varName, c); + assertEquals("Variables was not declared", 1, concreteInterpreter.getVariables().size()); Set<String> vars = Sets.newHashSet(); - vars.addAll(interpreter.getVariables().keySet()); + vars.addAll(concreteInterpreter.getVariables().keySet()); vars.add("thisEObject"); vars.add("self"); @@ -461,13 +432,13 @@ public class AcceleoMTLCompletionTests extends TestCase { private <T> void doTestAcceleoMTLCompletionWithDependencies(EClass c, T context1, T context2, T context3, Function<T, List<ContentProposal>> getProposalFunction, Collection<String> dependencies) { List<String> mockVsms = new ArrayList<String>(); mockVsms.add(SiriusTestsPlugin.PLUGIN_ID); - interpreter.setProperty(IInterpreter.FILES, mockVsms); + concreteInterpreter.setProperty(IInterpreter.FILES, mockVsms); for (String dep : dependencies) { - interpreter.addImport(dep); + concreteInterpreter.addImport(dep); } Set<String> vars = Sets.newHashSet(); - vars.addAll(interpreter.getVariables().keySet()); + vars.addAll(concreteInterpreter.getVariables().keySet()); vars.add("thisEObject"); vars.add("self"); @@ -525,11 +496,11 @@ public class AcceleoMTLCompletionTests extends TestCase { private <T> void doTestAcceleoMTLCompletionWithProposalStart(EClass c, CreateContextWithCursorPosition<T> createContextFunction, Function<T, List<ContentProposal>> getProposalFunction) { List<String> mockVsms = new ArrayList<String>(); mockVsms.add(SiriusTestsPlugin.PLUGIN_ID); - interpreter.setProperty(IInterpreter.FILES, mockVsms); - interpreter.addImport(IMPORT); + concreteInterpreter.setProperty(IInterpreter.FILES, mockVsms); + concreteInterpreter.addImport(IMPORT); Set<String> vars = Sets.newHashSet(); - vars.addAll(interpreter.getVariables().keySet()); + vars.addAll(concreteInterpreter.getVariables().keySet()); vars.add("thisEObject"); vars.add("self"); @@ -551,20 +522,6 @@ public class AcceleoMTLCompletionTests extends TestCase { checkCompletionProposal(c.eClass(), contentProposals, vars, false, Collections.singleton(IMPORT), "nam"); } - private ContentContext createContentContext(String expression, int cursorPostion, String eClass) { - return createContentContext(expression, cursorPostion, eClass, Collections.<String, String> emptyMap()); - } - - private ContentContext createContentContext(String expression, int cursorPostion, String eClass, Map<String, String> variables) { - return createContentContext(expression, cursorPostion, eClass, null, variables, Collections.<String> emptyList()); - } - - private ContentContext createContentContext(String expression, int cursorPostion, String eClass, EPackage ePackage, Map<String, String> variables, Collection<String> dependencies) { - Collection<EPackage> pList = ePackage == null ? Collections.<EPackage> emptyList() : Collections.singletonList(ePackage); - return new ContentContext(expression, cursorPostion, DefaultInterpreterContextFactory.createInterpreterContext(null, true, null, Collections.singletonList(eClass), pList, variables, - dependencies)); - } - private void checkCompletionProposal(EClass eClass, List<ContentProposal> contentProposals, Set<String> variables, boolean ignoreOperationNotFound) { checkCompletionProposal(eClass, contentProposals, variables, ignoreOperationNotFound, Collections.<String> emptyList(), ""); } @@ -590,7 +547,7 @@ public class AcceleoMTLCompletionTests extends TestCase { checkEStruturalFeatures(eClass, proposals, concerned, errorMsg); - checkEOperations(eClass, implicitContext, proposals, concerned, errorMsg); + checkEOperations(eClass, implicitContext, proposals, concerned, getSignature, errorMsg); checkDependencies(implicitContext, dependencies, proposals, concerned, errorMsg); @@ -625,65 +582,6 @@ public class AcceleoMTLCompletionTests extends TestCase { } } - private StringBuilder lookForExpectedProposals(Iterable<String> expectedProposals, Collection<String> proposals, Predicate<String> concerned) { - StringBuilder tmpMsg = new StringBuilder(); - - for (String prop : Iterables.filter(expectedProposals, concerned)) { - if (proposals.contains(prop)) { - proposals.remove(prop); - } else { - tmpMsg.append("\n * " + prop); - } - } - - return tmpMsg; - } - - private void checkEOperations(EClass eClass, boolean implicitContext, Collection<String> proposals, Predicate<String> concerned, StringBuilder errorMsg) { - // EOperations - Collection<EOperation> opToCheck = Lists.newArrayList(); - Collection<String> opNames = Sets.newHashSet(); - for (EOperation op : eClass.getEAllOperations()) { - if (!(implicitContext && opNames.contains(op.getName()))) { - opNames.add(op.getName()); - opToCheck.add(op); - } - } - - StringBuilder tmpMsg = lookForExpectedProposals(Iterables.transform(opToCheck, getSignature), proposals, concerned); - - if (tmpMsg.length() != 0) { - tmpMsg.insert(0, "\nSome expected operations are not present in completion proposals:"); - errorMsg.append(tmpMsg.toString()); - } - } - - private void checkEStruturalFeatures(EClass eClass, Collection<String> proposals, Predicate<String> concerned, StringBuilder errorMsg) { - Function<EStructuralFeature, String> getExpectedProposal = new Function<EStructuralFeature, String>() { - public String apply(EStructuralFeature from) { - return from.getName(); - } - }; - - // EStructuralfeatures - StringBuilder tmpMsg = lookForExpectedProposals(Iterables.transform(eClass.getEAllStructuralFeatures(), getExpectedProposal), proposals, concerned); - - if (tmpMsg.length() != 0) { - tmpMsg.insert(0, "\nSome expected features are not present in completion proposals:"); - errorMsg.append(tmpMsg.toString()); - } - } - - private void checkVariables(Set<String> variables, Collection<String> proposals, Predicate<String> concerned, StringBuilder errorMsg) { - // Variables - StringBuilder tmpMsg = lookForExpectedProposals(variables, proposals, concerned); - - if (tmpMsg.length() != 0) { - tmpMsg.insert(0, "\nSome expected variables are not present in completion proposals:"); - errorMsg.append(tmpMsg.toString()); - } - } - private String getProposalSignature(EOperation op) { String opSig = op.getName() + "("; if (op.getEParameters().size() > 1) { @@ -722,14 +620,6 @@ public class AcceleoMTLCompletionTests extends TestCase { } - private Collection<String> extractProposal(List<ContentProposal> proposals) { - return Lists.newArrayList(Iterables.transform(proposals, new Function<ContentProposal, String>() { - public String apply(ContentProposal from) { - return from.getProposal(); - } - })); - } - private abstract class CreateContextWithCursorPosition<T> implements Function<String, T> { int i = 0; diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/feature/FeatureCompletionTests.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/feature/FeatureCompletionTests.java new file mode 100644 index 0000000000..966784d503 --- /dev/null +++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/feature/FeatureCompletionTests.java @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 2010, 2014 THALES GLOBAL SERVICES. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.tests.unit.common.interpreter.feature; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EcoreFactory; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.sirius.common.tools.api.contentassist.ContentContext; +import org.eclipse.sirius.common.tools.api.contentassist.ContentInstanceContext; +import org.eclipse.sirius.common.tools.api.contentassist.ContentProposal; +import org.eclipse.sirius.common.tools.internal.interpreter.FeatureInterpreter; +import org.eclipse.sirius.common.ui.tools.internal.interpreter.FeatureProposalProvider; +import org.eclipse.sirius.diagram.DNode; +import org.eclipse.sirius.diagram.DiagramFactory; +import org.eclipse.sirius.diagram.DiagramPackage; +import org.eclipse.sirius.tests.unit.common.interpreter.AbstractCompletionTestCase; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; + +/** + * Tests for the {@link FeatureInterpreter} utility class. + * + * @author mporhel + * + */ +public class FeatureCompletionTests extends AbstractCompletionTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + setInterpreterAndProposalProvider(new FeatureInterpreter(), new FeatureProposalProvider()); + }; + + /** + * Tests proposal based on "feature:" prefix. + */ + public void testCompletionOnInterpreterPrefix() { + ContentContext cc = createContentContext("f", 1, "EClass"); + assertCompletionMatchesEmptyExpression(cc, compoundProposalFunction); + + cc = createContentContext("feature", 7, "EClass"); + assertCompletionMatchesEmptyExpression(cc, compoundProposalFunction); + } + + /** + * Tests proposal based on "feature:" prefix. + */ + public void testInstanceCompletionOnInterpreterPrefix() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + c.setName("c1"); + + ContentInstanceContext cic = new ContentInstanceContext(c, "f", 1); + assertCompletionMatchesEmptyExpression(cic, compoundProposalInstanceFunction); + + cic = new ContentInstanceContext(c, "feature", 7); + assertCompletionMatchesEmptyExpression(cic, compoundProposalInstanceFunction); + } + + /** + * Tests that completion proposals. + */ + public void testFeatureProposals() { + EClass eClass = EcoreFactory.eINSTANCE.createEClass(); + eClass.setName("c"); + + ContentContext cc = null; + assertTrue(getProposals(cc).isEmpty()); + + ContentInstanceContext cic = null; + assertTrue(getProposals(cic).isEmpty()); + } + + /** + * Tests emtpy expression proposal. + */ + public void testCompletionEmtpyField() { + ContentContext cc = createContentContext("", 0, "EClass"); + assertCompletionMatchesEmptyExpression(cc, proposalFunction); + } + + /** + * Tests emtpy expression proposal. + */ + public void testInstanceCompletionEmtpyField() { + ContentInstanceContext cic = new ContentInstanceContext(null, "", 0); + assertCompletionMatchesEmptyExpression(cic, proposalInstanceFunction); + } + + /** + * Tests completion for an EClass. + */ + public void testNoCompletion() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + c.setName("c1"); + + Function<Integer, ContentContext> createEmptyExpressionContextWithCursor = new Function<Integer, ContentContext>() { + public ContentContext apply(Integer input) { + return createContentContext("feature:", input, "EClass"); + } + }; + doTestNoCompletion(createEmptyExpressionContextWithCursor, proposalFunction); + } + + /** + * Tests completion for an EClass. + */ + public void testInstanceNoCompletion() { + final EClass c = EcoreFactory.eINSTANCE.createEClass(); + c.setName("c1"); + + Function<Integer, ContentInstanceContext> createEmptyExpressionContextWithCursor = new Function<Integer, ContentInstanceContext>() { + public ContentInstanceContext apply(Integer input) { + return new ContentInstanceContext(c, "feature:", input); + } + }; + doTestNoCompletion(createEmptyExpressionContextWithCursor, proposalInstanceFunction); + } + + /** + * @param createContextFunction + * function to give a context with "feature:" and the wanted + * cursor position. + */ + private <T> void doTestNoCompletion(Function<Integer, T> createContextFunction, Function<T, List<ContentProposal>> getProposalFunction) { + // No completion in between 'f' and ':' in 'feature:' + for (int i = 0; i < 8; i++) { + T ctx = createContextFunction.apply(i); + List<ContentProposal> contentProposals = getProposalFunction.apply(ctx); + assertTrue("No completion should be proposed when cursor is between 'f' and ':' in 'feature:' for i=" + i, contentProposals.isEmpty()); + } + } + + /** + * Tests completion for an EClass. + */ + public void testCompletionOnEcore() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + ContentContext cc = createContentContext("feature:", 8, "EClass", EcorePackage.eINSTANCE); + + // implicit context + List<ContentProposal> contentProposals = getProposals(cc); + + checkCompletionProposal(c.eClass(), contentProposals); + } + + /** + * Tests completion for an EClass. + */ + public void testInstanceCompletionOnEcore() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + ContentInstanceContext cic = new ContentInstanceContext(c, "feature:", 8); + + // implicit context + List<ContentProposal> contentProposals = getProposals(cic); + + checkCompletionProposal(c.eClass(), contentProposals); + } + + /** + * Tests completion for an EClass. + */ + public void testCompletionOnOtherM2() { + DNode dNode = DiagramFactory.eINSTANCE.createDNode(); + ContentContext cc = createContentContext("feature:", 8, "DNode", DiagramPackage.eINSTANCE); + List<ContentProposal> contentProposals = getProposals(cc); + + checkCompletionProposal(dNode.eClass(), contentProposals); + } + + /** + * Tests completion for an EClass. + */ + public void testInstanceCompletionOnOtherM2() { + DNode dNode = DiagramFactory.eINSTANCE.createDNode(); + ContentInstanceContext cic = new ContentInstanceContext(dNode, "feature:", 8); + + List<ContentProposal> contentProposals = getProposals(cic); + + checkCompletionProposal(dNode.eClass(), contentProposals); + } + + private void checkCompletionProposal(EClass eClass, List<ContentProposal> contentProposals) { + Collection<String> proposals = extractProposal(contentProposals); + + StringBuilder errorMsg = new StringBuilder(); + + Predicate<String> concerned = new Predicate<String>() { + public boolean apply(String input) { + return true; + } + }; + + checkEStruturalFeatures(eClass, proposals, concerned, errorMsg); + + assertTrue(errorMsg.toString(), 0 == errorMsg.length()); + } +} diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/ocl/OCLCompletionTest.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/ocl/OCLCompletionTest.java index 7cabf1faa6..90a450a4cc 100644 --- a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/ocl/OCLCompletionTest.java +++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/ocl/OCLCompletionTest.java @@ -15,23 +15,21 @@ import java.util.Collection; import java.util.List; import java.util.Set; -import junit.framework.TestCase; - import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EOperation; -import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EcoreFactory; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.sirius.common.ocl.business.internal.interpreter.OclInterpreter; import org.eclipse.sirius.common.tools.api.contentassist.ContentContext; import org.eclipse.sirius.common.tools.api.contentassist.ContentInstanceContext; import org.eclipse.sirius.common.tools.api.contentassist.ContentProposal; -import org.eclipse.sirius.common.tools.api.contentassist.IProposalProvider; import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; import org.eclipse.sirius.common.tools.api.util.StringUtil; import org.eclipse.sirius.diagram.DNode; import org.eclipse.sirius.diagram.DiagramFactory; +import org.eclipse.sirius.tests.SiriusTestsPlugin; import org.eclipse.sirius.tests.support.api.PluginVersionCompatibility; +import org.eclipse.sirius.tests.unit.common.interpreter.AbstractCompletionTestCase; import org.osgi.framework.Version; import com.google.common.base.Function; @@ -40,8 +38,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import org.eclipse.sirius.tests.SiriusTestsPlugin; - /** * Ensures that the OCL interpreter provides correct completion (e.g. in the * Interpreter View). @@ -49,20 +45,18 @@ import org.eclipse.sirius.tests.SiriusTestsPlugin; * @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a> * */ -public class OCLCompletionTest extends TestCase { - - private OclInterpreter interpreter; - - private IProposalProvider proposalProvider; +public class OCLCompletionTest extends AbstractCompletionTestCase { /** - * A function that, once applied, returns all the proposals according to a - * given {@link ContentInstanceContext}. + * + * {@inheritDoc} + * + * @see junit.framework.TestCase#setUp() */ - private Function<ContentInstanceContext, List<ContentProposal>> proposalInstanceFunction = new Function<ContentInstanceContext, List<ContentProposal>>() { - public List<ContentProposal> apply(ContentInstanceContext input) { - return proposalProvider.getProposals(interpreter, input); - } + protected void setUp() throws Exception { + super.setUp(); + OclInterpreter oclInterpreter = new OclInterpreter(); + setInterpreterAndProposalProvider(oclInterpreter, oclInterpreter); }; /** @@ -76,33 +70,28 @@ public class OCLCompletionTest extends TestCase { }; /** - * - * {@inheritDoc} - * - * @see junit.framework.TestCase#setUp() + * Tests proposal based on "ocl:" prefix. */ - protected void setUp() throws Exception { - super.setUp(); + public void testOCLCompletionOnInterpreterPrefix() { + ContentContext cc = createContentContext("o", 1, "EClass"); + assertCompletionMatchesEmptyExpression(cc, compoundProposalFunction); - // The OCL Interpreter is its own proposal provider (as it implements - // IProposalProvider) - interpreter = new OclInterpreter(); - proposalProvider = interpreter; - }; + cc = createContentContext("ocl", 3, "EClass"); + assertCompletionMatchesEmptyExpression(cc, compoundProposalFunction); + } /** - * - * {@inheritDoc} - * - * @see junit.framework.TestCase#tearDown() + * Tests proposal based on "ocl:" prefix. */ - @Override - protected void tearDown() throws Exception { - interpreter.dispose(); - interpreter = null; - proposalProvider = null; + public void testOCLInstanceOnCompletionInterpreterPrefix() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + c.setName("c1"); + + ContentInstanceContext cic = new ContentInstanceContext(c, "o", 1); + assertCompletionMatchesEmptyExpression(cic, compoundProposalInstanceFunction); - super.tearDown(); + cic = new ContentInstanceContext(c, "ocl", 3); + assertCompletionMatchesEmptyExpression(cic, compoundProposalInstanceFunction); } /** @@ -111,13 +100,13 @@ public class OCLCompletionTest extends TestCase { */ public void testOCLProposalsOnNullContext() { ContentContext cc = null; - assertTrue(proposalProvider.getProposals(interpreter, cc).isEmpty()); + assertTrue(getProposals(cc).isEmpty()); ContentInstanceContext cic = null; - assertTrue(proposalProvider.getProposals(interpreter, cic).isEmpty()); + assertTrue(getProposals(cic).isEmpty()); cic = new ContentInstanceContext(null, OclInterpreter.OCL_DISCRIMINANT, OclInterpreter.OCL_DISCRIMINANT.length()); - assertTrue(proposalProvider.getProposals(interpreter, cic).isEmpty()); + assertTrue(getProposals(cic).isEmpty()); } /** @@ -158,21 +147,21 @@ public class OCLCompletionTest extends TestCase { ContentInstanceContext cic = new ContentInstanceContext(c, OclInterpreter.OCL_DISCRIMINANT, 0); // No completion before ocl: - List<ContentProposal> contentProposals = proposalProvider.getProposals(interpreter, cic); + List<ContentProposal> contentProposals = getProposals(cic); assertTrue(contentProposals.isEmpty()); // Check completion on 'ocl:' cic = new ContentInstanceContext(c, OclInterpreter.OCL_DISCRIMINANT, OclInterpreter.OCL_DISCRIMINANT.length()); - contentProposals = proposalProvider.getProposals(interpreter, cic); + contentProposals = getProposals(cic); Set<String> vars = Sets.newHashSet(); - vars.addAll(interpreter.getVariables().keySet()); + vars.addAll(concreteInterpreter.getVariables().keySet()); vars.add("self"); checkCompletionProposal(c.eClass(), contentProposals, vars); // Check completion on 'ocl:self.' (should be the same except for 'self' cic = new ContentInstanceContext(c, OclInterpreter.OCL_DISCRIMINANT + "self.", 9); vars.remove("self"); - contentProposals = proposalProvider.getProposals(interpreter, cic); + contentProposals = getProposals(cic); checkCompletionProposal(c.eClass(), contentProposals, vars); } @@ -185,21 +174,21 @@ public class OCLCompletionTest extends TestCase { ContentInstanceContext cic = new ContentInstanceContext(dNode, OclInterpreter.OCL_DISCRIMINANT, 0); // No completion before ocl: - List<ContentProposal> contentProposals = proposalProvider.getProposals(interpreter, cic); + List<ContentProposal> contentProposals = getProposals(cic); assertTrue(contentProposals.isEmpty()); // Check completion on 'ocl:' cic = new ContentInstanceContext(dNode, OclInterpreter.OCL_DISCRIMINANT, OclInterpreter.OCL_DISCRIMINANT.length()); - contentProposals = proposalProvider.getProposals(interpreter, cic); + contentProposals = getProposals(cic); Set<String> vars = Sets.newHashSet(); vars.add("self"); checkCompletionProposal(dNode.eClass(), contentProposals, vars); // Check completion on 'ocl:self.' (should be the same except for 'self' cic = new ContentInstanceContext(dNode, OclInterpreter.OCL_DISCRIMINANT + "self./", 9); - contentProposals = proposalProvider.getProposals(interpreter, cic); + contentProposals = getProposals(cic); - checkCompletionProposal(dNode.eClass(), contentProposals, interpreter.getVariables().keySet()); + checkCompletionProposal(dNode.eClass(), contentProposals, concreteInterpreter.getVariables().keySet()); } /** @@ -217,9 +206,9 @@ public class OCLCompletionTest extends TestCase { }; List<String> mockVsms = new ArrayList<String>(); mockVsms.add(SiriusTestsPlugin.PLUGIN_ID); - interpreter.setProperty(IInterpreter.FILES, mockVsms); + concreteInterpreter.setProperty(IInterpreter.FILES, mockVsms); Set<String> vars = Sets.newHashSet(); - vars.addAll(interpreter.getVariables().keySet()); + vars.addAll(concreteInterpreter.getVariables().keySet()); vars.add("self"); // Step 2: call completion on 'ocl:ab' (should return 'abstract') @@ -310,7 +299,7 @@ public class OCLCompletionTest extends TestCase { StringBuilder errorMsg = new StringBuilder(); checkVariables(variables, proposals, concerned, errorMsg); checkEStruturalFeatures(eClass, proposals, concerned, errorMsg); - checkEOperations(eClass, proposals, concerned, errorMsg); + checkEOperations(eClass, true, proposals, concerned, getSignature, errorMsg); checkStandardOCLOperations(proposals, concerned, errorMsg); assertTrue(errorMsg.toString(), 0 == errorMsg.length()); @@ -321,106 +310,6 @@ public class OCLCompletionTest extends TestCase { } /** - * Ensures that the given proposal lists contain all the eOperations of the - * given {@link EClass} starting with the expected prefix. - * - * @param eClass - * the EClass on which completion is called - * @param proposals - * the proposals computed by the OCL interpreter - * @param concerned - * a predicated allowing to select only the elements starting - * with the expected prefix - * @param errorMsg - * the errorMsg to use for listing missing proposals - */ - private void checkEOperations(EClass eClass, Collection<String> proposals, Predicate<String> concerned, StringBuilder errorMsg) { - // Step 1: get the EOperations contained in the given eClass - Collection<EOperation> opToCheck = Lists.newArrayList(); - Collection<String> opNames = Sets.newHashSet(); - for (EOperation op : eClass.getEAllOperations()) { - if (!(opNames.contains(op.getName()))) { - opNames.add(op.getName()); - opToCheck.add(op); - } - } - Iterable<String> expectedEOperationProposals = Iterables.transform(opToCheck, getSignature); - - // Step 2: filtering the EOperations according to proposal start - expectedEOperationProposals = Iterables.filter(expectedEOperationProposals, concerned); - - // Step 3: ensure that proposals contain all the expected elements - StringBuilder tmpMsg = lookForExpectedProposals(expectedEOperationProposals, proposals); - - if (tmpMsg.length() != 0) { - tmpMsg.insert(0, "\nSome expected operations are not present in completion proposals:"); - errorMsg.append(tmpMsg.toString()); - } - } - - /** - * Ensures that the given proposal lists contain all the eStructuralFeatures - * of the given {@link EClass} starting with the expected prefix. - * - * @param eClass - * the EClass on which completion is called - * @param proposals - * the proposals computed by the OCL interpreter - * @param concerned - * a predicated allowing to select only the elements starting - * with the expected prefix - * @param errorMsg - * the errorMsg to use for listing missing proposals - */ - private void checkEStruturalFeatures(EClass eClass, Collection<String> proposals, Predicate<String> concerned, StringBuilder errorMsg) { - // Step 1: get all EClasses structural features - Function<EStructuralFeature, String> getExpectedProposal = new Function<EStructuralFeature, String>() { - public String apply(EStructuralFeature from) { - return from.getName(); - } - }; - Iterable<String> expectedStructuralFeatures = Iterables.transform(eClass.getEAllStructuralFeatures(), getExpectedProposal); - - // Step 2: filtering features according to proposal start - expectedStructuralFeatures = Sets.newLinkedHashSet(Iterables.filter(expectedStructuralFeatures, concerned)); - - // Step 3: ensure that proposals contain all the expected elements - StringBuilder tmpMsg = lookForExpectedProposals(expectedStructuralFeatures, proposals); - - if (tmpMsg.length() != 0) { - tmpMsg.insert(0, "\nSome expected features are not present in completion proposals:"); - errorMsg.append(tmpMsg.toString()); - } - } - - /** - * Ensures that the given proposal lists contain all the given expected - * variables. - * - * @param variables - * the expected variables - * @param proposals - * the proposals computed by the OCL interpreter - * @param concerned - * a predicated allowing to select only the elements starting - * with the expected prefix - * @param errorMsg - * the errorMsg to use for listing missing proposals - */ - private void checkVariables(Set<String> variables, Collection<String> proposals, Predicate<String> concerned, StringBuilder errorMsg) { - // Step 1: filtering variable according to proposal start - Set<String> filteredVariables = Sets.newLinkedHashSet(Iterables.filter(variables, concerned)); - - // Step 2: ensure that proposals list all the expected variables - StringBuilder tmpMsg = lookForExpectedProposals(filteredVariables, proposals); - - if (tmpMsg.length() != 0) { - tmpMsg.insert(0, "\nSome expected variables are not present in completion proposals:"); - errorMsg.append(tmpMsg.toString()); - } - } - - /** * Ensures that the given proposal lists contain all the standard OCL * operations (oclAsType...) and toString. * @@ -442,11 +331,8 @@ public class OCLCompletionTest extends TestCase { oclOperations.add("toString"); } - // Step 2: filter expected operations according to proposal start - oclOperations = Sets.newLinkedHashSet(Iterables.filter(oclOperations, concerned)); - // Step 3: ensure that proposals list all those operations - StringBuilder tmpMsg = lookForExpectedProposals(oclOperations, proposals); + StringBuilder tmpMsg = lookForExpectedProposals(oclOperations, proposals, concerned); if (tmpMsg.length() != 0) { tmpMsg.insert(0, "\nSome expected variables are not present in completion proposals:"); @@ -455,30 +341,6 @@ public class OCLCompletionTest extends TestCase { } /** - * Ensures that the given expected proposals are all contained in the given - * actual proposals. - * - * @param expectedProposals - * the expected proposals - * @param proposals - * the proposals computed by the OCL interpreter - * @return the error message - */ - private StringBuilder lookForExpectedProposals(Iterable<String> expectedProposals, Collection<String> proposals) { - StringBuilder tmpMsg = new StringBuilder(); - - for (String prop : expectedProposals) { - if (proposals.contains(prop)) { - proposals.remove(prop); - } else { - tmpMsg.append("\n * " + prop); - } - } - - return tmpMsg; - } - - /** * Returns the given collection of {@link ContentProposal} as a list of * String containing the values of each proposal. * diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/service/ServiceCompletionTests.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/service/ServiceCompletionTests.java new file mode 100644 index 0000000000..12e96fdfc9 --- /dev/null +++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/service/ServiceCompletionTests.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) 2010, 2014 THALES GLOBAL SERVICES. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.tests.unit.common.interpreter.service; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EcoreFactory; +import org.eclipse.sirius.common.tools.api.contentassist.ContentContext; +import org.eclipse.sirius.common.tools.api.contentassist.ContentInstanceContext; +import org.eclipse.sirius.common.tools.api.contentassist.ContentProposal; +import org.eclipse.sirius.common.tools.internal.interpreter.ServiceInterpreter; +import org.eclipse.sirius.common.ui.tools.internal.interpreter.ServiceProposalProvider; +import org.eclipse.sirius.diagram.DNode; +import org.eclipse.sirius.diagram.DiagramFactory; +import org.eclipse.sirius.diagram.DiagramPackage; +import org.eclipse.sirius.tests.unit.common.interpreter.AbstractCompletionTestCase; + +import com.google.common.base.Predicate; +import com.google.common.collect.Sets; + +/** + * Tests for the {@link ServiceInterpreter} utility class. + * + * @author mporhel + * + */ +public class ServiceCompletionTests extends AbstractCompletionTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + setInterpreterAndProposalProvider(new ServiceInterpreter(), new ServiceProposalProvider()); + }; + + /** + * Tests proposal based on "service:" prefix. + */ + public void testCompletionOnInterpreterPrefix() { + ContentContext cc = createContentContext("s", 1, "EClass"); + assertCompletionMatchesEmptyExpression(cc, compoundProposalFunction); + + cc = createContentContext("service", 7, "EClass"); + assertCompletionMatchesEmptyExpression(cc, compoundProposalFunction); + } + + /** + * Tests proposal based on "service:" prefix. + */ + public void testInstanceCompletionOnInterpreterPrefix() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + c.setName("c1"); + + ContentInstanceContext cic = new ContentInstanceContext(c, "s", 1); + assertCompletionMatchesEmptyExpression(cic, compoundProposalInstanceFunction); + + cic = new ContentInstanceContext(c, "service", 7); + assertCompletionMatchesEmptyExpression(cic, compoundProposalInstanceFunction); + } + + /** + * Tests that completion proposals. + */ + public void testProposals() { + EClass eClass = EcoreFactory.eINSTANCE.createEClass(); + eClass.setName("c"); + + ContentContext cc = null; + assertTrue(getProposals(cc).isEmpty()); + + ContentInstanceContext cic = null; + assertTrue(getProposals(cic).isEmpty()); + } + + /** + * Tests empty expression proposal. + */ + public void testCompletionEmtpyField() { + ContentContext cc = createContentContext("", 0, "EClass"); + assertCompletionMatchesEmptyExpression(cc, proposalFunction); + } + + /** + * Tests empty expression proposal. + */ + public void testInstanceCompletionEmtpyField() { + ContentInstanceContext cic = new ContentInstanceContext(null, "", 0); + assertCompletionMatchesEmptyExpression(cic, proposalInstanceFunction); + } + + /** + * Tests completion for an EClass. + */ + public void testCompletionOnEcore() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + ContentContext cc = createContentContext("service:", 8, c, "EClass"); + + // implicit context + List<ContentProposal> contentProposals = getProposals(cc); + + // ServiceProposalProvider add "." at the end of a variable + Set<String> vars = Sets.newHashSet(); + for (String variable : concreteInterpreter.getVariables().keySet()) { + vars.add(variable + "."); + } + vars.add("self."); + + checkCompletionProposal(c.eClass(), contentProposals, vars); + } + + /** + * Tests completion for an EClass. + */ + public void testInstanceCompletionOnEcore() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + ContentInstanceContext cic = new ContentInstanceContext(c, "service:", 8); + + List<ContentProposal> contentProposals = getProposals(cic); + + // ServiceProposalProvider add "." at the end of a variable + Set<String> vars = Sets.newHashSet(); + for (String variable : concreteInterpreter.getVariables().keySet()) { + vars.add(variable + "."); + } + vars.add("self."); + + checkCompletionProposal(c.eClass(), contentProposals, vars); + } + + /** + * Tests completion for an EClass. + */ + public void testCompletionOnOtherM2() { + + DNode dNode = DiagramFactory.eINSTANCE.createDNode(); + ContentContext cc = createContentContext("service:", 8, dNode, "DNode", DiagramPackage.eINSTANCE); + + List<ContentProposal> contentProposals = getProposals(cc); + + Set<String> vars = Sets.newHashSet(); + + // ServiceProposalProvider add "." at the end of a variable + vars.add("self."); + + checkCompletionProposal(dNode.eClass(), contentProposals, vars); + } + + /** + * Tests completion for an EClass. + */ + public void testInstanceCompletionOnOtherM2() { + DNode dNode = DiagramFactory.eINSTANCE.createDNode(); + ContentInstanceContext cic = new ContentInstanceContext(dNode, "service:", 8); + List<ContentProposal> contentProposals = getProposals(cic); + + // ServiceProposalProvider add "." at the end of a variable + Set<String> vars = Sets.newHashSet(); + for (String variable : concreteInterpreter.getVariables().keySet()) { + vars.add(variable + "."); + } + vars.add("self."); + + checkCompletionProposal(dNode.eClass(), contentProposals, vars); + } + + private void checkCompletionProposal(EClass eClass, List<ContentProposal> contentProposals, Set<String> variables) { + Collection<String> proposals = extractProposal(contentProposals); + StringBuilder errorMsg = new StringBuilder(); + + Predicate<String> concerned = new Predicate<String>() { + public boolean apply(String input) { + return true; + } + }; + + checkVariables(variables, proposals, concerned, errorMsg); + assertTrue(errorMsg.toString(), 0 == errorMsg.length()); + } +} diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/variable/VariableCompletionTests.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/variable/VariableCompletionTests.java new file mode 100644 index 0000000000..11311b32a7 --- /dev/null +++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/interpreter/variable/VariableCompletionTests.java @@ -0,0 +1,224 @@ +/******************************************************************************* + * Copyright (c) 2010, 2014 THALES GLOBAL SERVICES. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.tests.unit.common.interpreter.variable; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EcoreFactory; +import org.eclipse.sirius.common.tools.api.contentassist.ContentContext; +import org.eclipse.sirius.common.tools.api.contentassist.ContentInstanceContext; +import org.eclipse.sirius.common.tools.api.contentassist.ContentProposal; +import org.eclipse.sirius.common.tools.internal.interpreter.VariableInterpreter; +import org.eclipse.sirius.common.ui.tools.internal.interpreter.VariableProposalProvider; +import org.eclipse.sirius.diagram.DNode; +import org.eclipse.sirius.diagram.DiagramFactory; +import org.eclipse.sirius.diagram.DiagramPackage; +import org.eclipse.sirius.tests.unit.common.interpreter.AbstractCompletionTestCase; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Sets; + +/** + * Tests for the {@link VariableInterpreter} utility class. + * + * @author mporhel + * + */ +public class VariableCompletionTests extends AbstractCompletionTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + setInterpreterAndProposalProvider(new VariableInterpreter(), new VariableProposalProvider()); + }; + + /** + * Tests proposal based on "var:" prefix. + */ + public void testCompletionOnInterpreterPrefix() { + + ContentContext cc = createContentContext("v", 1, "EClass"); + assertCompletionMatchesEmptyExpression(cc, compoundProposalFunction); + + cc = createContentContext("var", 3, "EClass"); + assertCompletionMatchesEmptyExpression(cc, compoundProposalFunction); + } + + /** + * Tests proposal based on "var:" prefix. + */ + public void testInstanceCompletionOnInterpreterPrefix() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + c.setName("c1"); + + ContentInstanceContext cic = new ContentInstanceContext(c, "v", 1); + assertCompletionMatchesEmptyExpression(cic, compoundProposalInstanceFunction); + + cic = new ContentInstanceContext(c, "var", 3); + assertCompletionMatchesEmptyExpression(cic, compoundProposalInstanceFunction); + } + + /** + * Tests that completion proposals. + */ + public void testProposals() { + EClass eClass = EcoreFactory.eINSTANCE.createEClass(); + eClass.setName("c"); + + ContentContext cc = null; + assertTrue(getProposals(cc).isEmpty()); + + ContentInstanceContext cic = null; + assertTrue(getProposals(cic).isEmpty()); + } + + /** + * Tests empty expression proposal. + */ + public void testCompletionEmtpyField() { + ContentContext cc = createContentContext("", 0, "EClass"); + assertCompletionMatchesEmptyExpression(cc, proposalFunction); + } + + /** + * Tests empty expression proposal. + */ + public void testInstanceCompletionEmtpyField() { + ContentInstanceContext cic = new ContentInstanceContext(null, "", 0); + assertCompletionMatchesEmptyExpression(cic, proposalInstanceFunction); + } + + /** + * Tests completion for an EClass. + */ + public void testNoCompletion() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + c.setName("c1"); + + Function<Integer, ContentContext> createEmptyExpressionContextWithCursor = new Function<Integer, ContentContext>() { + public ContentContext apply(Integer input) { + return createContentContext("var:", input, "EClass"); + } + }; + doTestNoCompletion(createEmptyExpressionContextWithCursor, proposalFunction); + } + + /** + * Tests completion for an EClass. + */ + public void testInstanceNoCompletion() { + final EClass c = EcoreFactory.eINSTANCE.createEClass(); + c.setName("c1"); + + Function<Integer, ContentInstanceContext> createEmptyExpressionContextWithCursor = new Function<Integer, ContentInstanceContext>() { + public ContentInstanceContext apply(Integer input) { + return new ContentInstanceContext(c, "var:", input); + } + }; + doTestNoCompletion(createEmptyExpressionContextWithCursor, proposalInstanceFunction); + } + + /** + * @param createContextFunction + * function to give a context with "var:" and the wanted cursor + * position. + */ + private <T> void doTestNoCompletion(Function<Integer, T> createContextFunction, Function<T, List<ContentProposal>> getProposalFunction) { + // No completion in between 'v' and ':' in 'var:' + for (int i = 0; i < 4; i++) { + T ctx = createContextFunction.apply(i); + List<ContentProposal> contentProposals = getProposalFunction.apply(ctx); + assertTrue("No completion should be proposed when cursor is between 'v' and ':' in 'var:' for i=" + i, contentProposals.isEmpty()); + } + } + + /** + * Tests completion for an EClass. + */ + public void testCompletionOnEcore() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + ContentContext cc = createContentContext("var:", 4, "EClass"); + + // implicit context + List<ContentProposal> contentProposals = getProposals(cc); + + Set<String> vars = Sets.newHashSet(); + vars.addAll(concreteInterpreter.getVariables().keySet()); + vars.add("self"); + + checkCompletionProposal(c.eClass(), contentProposals, vars); + } + + /** + * Tests completion for an EClass. + */ + public void testInstanceCompletionOnEcore() { + EClass c = EcoreFactory.eINSTANCE.createEClass(); + ContentInstanceContext cic = new ContentInstanceContext(c, "var:", 4); + + List<ContentProposal> contentProposals = getProposals(cic); + + Set<String> vars = Sets.newHashSet(); + vars.addAll(concreteInterpreter.getVariables().keySet()); + vars.add("self"); + + checkCompletionProposal(c.eClass(), contentProposals, vars); + } + + /** + * Tests completion for an EClass. + */ + public void testCompletionOnOtherM2() { + + DNode dNode = DiagramFactory.eINSTANCE.createDNode(); + ContentContext cc = createContentContext("var:", 4, "DNode", DiagramPackage.eINSTANCE); + + List<ContentProposal> contentProposals = getProposals(cc); + + Set<String> vars = Sets.newHashSet(); + vars.add("self"); + + checkCompletionProposal(dNode.eClass(), contentProposals, vars); + } + + /** + * Tests completion for an EClass. + */ + public void testInstanceCompletionOnOtherM2() { + DNode dNode = DiagramFactory.eINSTANCE.createDNode(); + ContentInstanceContext cic = new ContentInstanceContext(dNode, "var:", 4); + List<ContentProposal> contentProposals = getProposals(cic); + + Set<String> vars = Sets.newHashSet(); + vars.addAll(concreteInterpreter.getVariables().keySet()); + vars.add("self"); + + checkCompletionProposal(dNode.eClass(), contentProposals, vars); + } + + private void checkCompletionProposal(EClass eClass, List<ContentProposal> contentProposals, Set<String> variables) { + Collection<String> proposals = extractProposal(contentProposals); + StringBuilder errorMsg = new StringBuilder(); + + Predicate<String> concerned = new Predicate<String>() { + public boolean apply(String input) { + return true; + } + }; + + checkVariables(variables, proposals, concerned, errorMsg); + assertTrue(errorMsg.toString(), 0 == errorMsg.length()); + } +} |
