| /******************************************************************************* |
| * Copyright (c) 2013 E.D.Willink and others. |
| * 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: |
| * E.D.Willink - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ocl.examples.test.xtext; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.emf.ecore.resource.URIConverter; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.jface.text.ITextViewer; |
| import org.eclipse.jface.text.contentassist.ICompletionProposal; |
| import org.eclipse.ocl.examples.xtext.tests.TestUIUtil; |
| import org.eclipse.ocl.examples.xtext.tests.TestUtil; |
| import org.eclipse.ocl.examples.xtext.tests.XtextTestCase; |
| import org.eclipse.ocl.pivot.utilities.OCL; |
| import org.eclipse.ocl.pivot.utilities.PivotConstants; |
| import org.eclipse.ocl.xtext.oclinecore.ui.OCLinEcoreUiModule; |
| import org.eclipse.ocl.xtext.oclinecore.ui.internal.OCLinEcoreActivator; |
| import org.eclipse.ocl.xtext.oclstdlib.ui.OCLstdlibUiModule; |
| import org.eclipse.ocl.xtext.oclstdlib.ui.internal.OCLstdlibActivator; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.ide.IDE; |
| import org.eclipse.ui.part.FileEditorInput; |
| import org.eclipse.xtext.ui.editor.XtextEditor; |
| import org.eclipse.xtext.ui.editor.contentassist.ConfigurableCompletionProposal; |
| import org.eclipse.xtext.ui.editor.contentassist.XtextContentAssistProcessor; |
| import org.eclipse.xtext.ui.editor.model.IXtextDocument; |
| import org.eclipse.xtext.ui.editor.templates.XtextTemplateProposal; |
| |
| import com.google.inject.Injector; |
| |
| /** |
| * Tests that completion proposals include expected results and/or exclude unwanted offerings. |
| * |
| * It is not intended to provide exhaustive testing of all possible proposals. |
| * |
| * It is assumed that the grammar-driven capabilities of Xtext will be generally correct. |
| * |
| * There we test first to conform that completion propsals exist and so detect whether some |
| * unwanted evolution has occurred in Xtext or its usage. |
| * |
| * Then we test known challenging cases especially regressions. |
| */ |
| @SuppressWarnings("null") |
| public class CompletionProposalTests extends XtextTestCase |
| { |
| public static interface IReferenceCompletionProposal |
| { |
| boolean covers(@NonNull ICompletionProposal aProposal); |
| } |
| |
| public static class ReferenceCompletionProposal implements IReferenceCompletionProposal |
| { |
| protected final @NonNull String name; |
| |
| public ReferenceCompletionProposal(@NonNull String name) { |
| this.name = name; |
| } |
| |
| @Override |
| public boolean covers(@NonNull ICompletionProposal aProposal) { |
| if (!name.equals(aProposal.getDisplayString())) { |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public String toString() { |
| return name; |
| } |
| } |
| |
| public static class ReferenceConfigurableCompletionProposal extends ReferenceCompletionProposal implements IReferenceCompletionProposal |
| { |
| public ReferenceConfigurableCompletionProposal(@NonNull String name) { |
| super(name); |
| } |
| |
| @Override |
| public boolean covers(@NonNull ICompletionProposal aProposal) { |
| if (!(aProposal instanceof ConfigurableCompletionProposal)) { |
| return false; |
| } |
| return super.covers(aProposal); |
| } |
| } |
| |
| public static class ReferenceXtextTemplateProposal extends ReferenceCompletionProposal |
| { |
| public ReferenceXtextTemplateProposal(@NonNull String name) { |
| super(name); |
| } |
| |
| @Override |
| public boolean covers(@NonNull ICompletionProposal aProposal) { |
| if (!(aProposal instanceof XtextTemplateProposal)) { |
| return false; |
| } |
| return super.covers(aProposal); |
| } |
| } |
| |
| public static final @NonNull IReferenceCompletionProposal abstractKeywordProposal = new ReferenceConfigurableCompletionProposal("abstract"); |
| public static final @NonNull IReferenceCompletionProposal annotationTemplateProposal = new ReferenceXtextTemplateProposal("Annotation - annotation declaration"); |
| public static final @NonNull IReferenceCompletionProposal selfKeywordProposal = new ReferenceConfigurableCompletionProposal(PivotConstants.SELF_NAME); |
| public static final @NonNull IReferenceCompletionProposal sNameProposal = new ReferenceConfigurableCompletionProposal("s"); |
| |
| public static void assertExcludes(ICompletionProposal[] actualProposals, IReferenceCompletionProposal expectedProposal) { |
| for (ICompletionProposal actualProposal : actualProposals) { |
| if (expectedProposal.covers(actualProposal)) { |
| fail("Unexpected completion proposal " + expectedProposal); |
| } |
| } |
| } |
| |
| public static void assertIncludes(ICompletionProposal[] actualProposals, IReferenceCompletionProposal expectedProposal) { |
| for (ICompletionProposal actualProposal : actualProposals) { |
| if (expectedProposal.covers(actualProposal)) { |
| return; |
| } |
| } |
| fail("Missing completion proposal " + expectedProposal); |
| } |
| |
| protected XtextContentAssistProcessor contentAssistProcessor = null; |
| protected XtextEditor editor = null; |
| |
| protected @NonNull FileEditorInput createEcoreFileEditorInput(@NonNull OCL ocl, @NonNull IContainer container, @NonNull String fileName, @NonNull String testDocument)throws IOException, CoreException { |
| String ecoreString = createEcoreString(ocl, fileName, testDocument, true); |
| InputStream inputStream = new URIConverter.ReadableInputStream(ecoreString, "UTF-8"); |
| FileEditorInput fileEditorInput = TestUIUtil.createFileEditorInput(container, fileName, inputStream); |
| return fileEditorInput; |
| } |
| |
| protected void doTearDown(XtextEditor editor) { |
| TestUIUtil.flushEvents(); |
| editor.close(false); |
| TestUIUtil.flushEvents(); |
| } |
| |
| public void doTestEditor(@NonNull String testContent, @Nullable IReferenceCompletionProposal[] expectedProposals, |
| @Nullable IReferenceCompletionProposal[] unexpectedProposals) throws Exception { |
| int cursorIndex = testContent.indexOf("$"); |
| String trueContent = testContent.replace("$", ""); |
| IXtextDocument document = editor.getDocument(); |
| document.set(trueContent); |
| ITextViewer viewer = editor.getInternalSourceViewer(); |
| ICompletionProposal[] actualProposals = contentAssistProcessor.computeCompletionProposals(viewer, cursorIndex); |
| if (expectedProposals != null) { |
| for (IReferenceCompletionProposal expectedProposal : expectedProposals) { |
| assertIncludes(actualProposals, expectedProposal); |
| } |
| } |
| if (unexpectedProposals != null) { |
| for (IReferenceCompletionProposal unexpectedProposal : unexpectedProposals) { |
| assertExcludes(actualProposals, unexpectedProposal); |
| } |
| } |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| TestUIUtil.suppressGitPrefixPopUp(); |
| super.setUp(); |
| } |
| |
| protected void doSetUp(@NonNull String editorId, Injector injector, @NonNull String fileName, @NonNull String initialContent) |
| throws CoreException, PartInitException, IOException { |
| contentAssistProcessor = injector.getInstance(XtextContentAssistProcessor.class); |
| InputStream inputStream = new URIConverter.ReadableInputStream(initialContent, "UTF-8"); |
| IProject project = TestUtil.createJavaProject("CompletionProposalTests"); |
| TestUtil.createIFile(TestUtil.createFolder(project, "META-INF"), "MANIFEST.MF", |
| "Manifest-Version: 1.0\n"+ |
| "Bundle-ManifestVersion: 2\n"+ |
| "Bundle-Name: CompletionProposalTests\n"+ |
| "Bundle-SymbolicName: CompletionProposalTests\n"+ |
| "Bundle-Version: 1.0.0.qualifier\n"+ |
| "Bundle-RequiredExecutionEnvironment: JavaSE-1.6\n"+ |
| "Require-Bundle: org.eclipse.ocl.pivot\n"+ |
| ""); |
| TestUtil.createIFile(project, ".classpath", |
| "<classpath>\n" + |
| " <classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6\"/>\n" + |
| " <classpathentry kind=\"con\" path=\"org.eclipse.pde.core.requiredPlugins\"/>\n" + |
| " <classpathentry kind=\"src\" path=\"src\"/>\n" + |
| " <classpathentry kind=\"output\" path=\"bin\"/>\n" + |
| " </classpath>\n"+ |
| ""); |
| TestUtil.createIFile(TestUtil.createFolder(project, "src"), "Test.java", |
| "import org.eclipse.emf.ecore.provider.*;\n"+ |
| "public class Test {}\n"+ |
| ""); |
| project.build(IncrementalProjectBuilder.FULL_BUILD, null); |
| FileEditorInput fileEditorInput = TestUIUtil.createFileEditorInput(project, fileName, inputStream); |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow(); |
| IWorkbenchPage page = activeWorkbenchWindow.getActivePage(); |
| editor = (XtextEditor) IDE.openEditor(page, fileEditorInput, editorId, true); |
| } |
| |
| public void testEditor_OCLinEcore_Completions() throws Exception { |
| Injector injector = OCLinEcoreActivator.getInstance().getInjector(OCLinEcoreActivator.ORG_ECLIPSE_OCL_XTEXT_OCLINECORE_OCLINECORE); |
| doSetUp(OCLinEcoreUiModule.EDITOR_ID, injector, "completion.oclinecore", "package test : test = 'test' {}"); |
| doTestEditor("package p : p = 'p' {$}", |
| new IReferenceCompletionProposal[]{abstractKeywordProposal, annotationTemplateProposal}, null); |
| doTestEditor("package p : p = 'p' { class C { invariant I:$}}", |
| new IReferenceCompletionProposal[]{selfKeywordProposal}, null); |
| doTestEditor("package p : p = 'p' { class C { property s:String; invariant I:self.$}}", |
| new IReferenceCompletionProposal[]{sNameProposal}, null/*new IReferenceCompletionProposal[]{selfKeywordProposal}*/); |
| doTearDown(editor); |
| } |
| |
| public void testEditor_OCLstdlib_Completions() throws Exception { |
| Injector injector = OCLstdlibActivator.getInstance().getInjector(OCLstdlibActivator.ORG_ECLIPSE_OCL_XTEXT_OCLSTDLIB_OCLSTDLIB); |
| doSetUp(OCLstdlibUiModule.EDITOR_ID, injector, "completion.oclstdlib", |
| "import 'http://www.eclipse.org/ocl/2015/Library';\n" + |
| "library ocl : ocl = 'http://www.eclipse.org/ocl/2015/Library' {\n" + |
| " type Complex : PrimitiveType {\n" + |
| " }\n" + |
| "}';\n"); |
| try { |
| IReferenceCompletionProposal proposal1a = new ReferenceConfigurableCompletionProposal("PrimitiveType"); |
| IReferenceCompletionProposal proposal1b = new ReferenceConfigurableCompletionProposal("{"); |
| doTestEditor( |
| "import 'http://www.eclipse.org/ocl/2015/Library';\n" + |
| "library ocl : ocl = 'http://www.eclipse.org/ocl/2015/Library' {\n" + |
| " type Complex : Primitive$ {\n" + |
| " }\n" + |
| "}';\n", |
| new IReferenceCompletionProposal[]{proposal1a, proposal1b}, null); |
| // |
| // Completion proposal that probably resolves to a Jar entry. |
| // |
| IReferenceCompletionProposal proposal2a = new ReferenceConfigurableCompletionProposal("org.eclipse.emf.common.util.Reflect"); |
| IReferenceCompletionProposal proposal2b = new ReferenceConfigurableCompletionProposal("org.eclipse.emf.common.util.ResourceLocator"); |
| doTestEditor( |
| "import 'http://www.eclipse.org/ocl/2015/Library';\n" + |
| "library ocl : ocl = 'http://www.eclipse.org/ocl/2015/Library' {\n" + |
| " type Complex : PrimitiveType {\n" + |
| " operation testing() : String => 'org.eclipse.emf.common.util.R$';\n" + |
| " }\n" + |
| "}';\n", |
| new IReferenceCompletionProposal[]{proposal2a, proposal2b}, null); |
| // |
| // Completion proposal that probably resolves to a folder entry. |
| // |
| IReferenceCompletionProposal proposal3a = new ReferenceConfigurableCompletionProposal("org.eclipse.ocl.pivot.internal.ids.OclInvalidTypeIdImpl"); |
| IReferenceCompletionProposal proposal3b = new ReferenceConfigurableCompletionProposal("org.eclipse.ocl.pivot.internal.ids.OclVoidTypeIdImpl"); |
| doTestEditor( |
| "import 'http://www.eclipse.org/ocl/2015/Library';\n" + |
| "library ocl : ocl = 'http://www.eclipse.org/ocl/2015/Library' {\n" + |
| " type Complex : PrimitiveType {\n" + |
| " operation testing() : String => 'org.eclipse.ocl.pivot.internal.ids.O$';\n" + |
| " }\n" + |
| "}';\n", |
| new IReferenceCompletionProposal[]{proposal3a, proposal3b}, null); |
| } |
| finally { |
| doTearDown(editor); |
| } |
| } |
| } |