/******************************************************************************* * Copyright 2011 Google Inc. All Rights Reserved. * * 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 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.google.gwt.eclipse.core.editors.java; import com.google.gcp.eclipse.testing.TestUtil; import com.google.gdt.eclipse.core.JavaProjectUtilities; import com.google.gdt.eclipse.core.pde.BundleUtilities; import com.google.gwt.eclipse.core.GWTPlugin; import com.google.gwt.eclipse.core.resources.GWTImages; import junit.framework.TestCase; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor; import org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposal; import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.swt.graphics.Image; import org.osgi.framework.Bundle; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.TreeSet; /** * Test cases for {@link JsniMethodBodyCompletionProposalComputer}. */ @SuppressWarnings("restriction") public class JsniMethodBodyCompletionProposalComputerTest extends TestCase { // NOTE: This test is disabled in the pom.xml file. // See https://code.google.com/p/google-plugin-for-eclipse/issues/detail?id=329 for details. /** * Images that the JSNI completion proposal computer depends on. */ private static final String IMAGE_IDS[] = new String[] { GWTImages.JSNI_DEFAULT_METHOD_SMALL, GWTImages.JSNI_PRIVATE_METHOD_SMALL, GWTImages.JSNI_PROTECTED_METHOD_SMALL, GWTImages.JSNI_PUBLIC_METHOD_SMALL}; private static void assertExpectedProposals(List<String> expectedCompletions, List<ICompletionProposal> actualCompletions, int numCharsToOverwrite) { Set<String> expectedSet = new TreeSet<String>(expectedCompletions); Set<String> actualSet = new TreeSet<String>(); for (ICompletionProposal actualCompletion : actualCompletions) { assertTrue(actualCompletion instanceof JavaCompletionProposal); actualSet.add(((JavaCompletionProposal) actualCompletion).getReplacementString()); assertEquals("Expected Overwrite: " + numCharsToOverwrite + "Actual Overwrite: " + ((JavaCompletionProposal) actualCompletion).getReplacementLength(), ((JavaCompletionProposal) actualCompletion).getReplacementLength(), numCharsToOverwrite); } assertTrue("Expected: " + expectedSet.toString() + "\nActual: " + actualSet.toString(), actualSet.containsAll(expectedSet) && expectedSet.containsAll(actualSet)); } private static void assertNoProposals(IProgressMonitor monitor, JsniMethodBodyCompletionProposalComputer jcpc, CompilationUnitEditor cuEditor, ISourceViewer viewer, int offset) { assertEquals(0, jcpc.computeCompletionProposals( new JavaContentAssistInvocationContext(viewer, offset, cuEditor), monitor).size()); } private static List<String> createJsniBlocks(IJavaProject javaProject, int indentationUnits, String... snippets) { List<String> blocks = new ArrayList<String>(); for (String snippet : snippets) { String jsniBlock = JsniMethodBodyCompletionProposalComputer.createJsniBlock( javaProject, snippet, indentationUnits); blocks.add(jsniBlock); } return blocks; } private static String synthesizeProjectNameForThisTest(TestCase test) { return (test.getClass().getCanonicalName() + "." + test.getName()).replace( '.', '_'); } // Validate all proposals for invocation index at end of second line. private static void validateExpectedProposals(IJavaProject javaProject, String fullyQualifiedClassName, String source, String... expectedProposals) throws CoreException, BadLocationException { validateExpectedProposals(javaProject, fullyQualifiedClassName, source, 1, 0, expectedProposals); } /** * If length of line at 'lineNum' is 'len', then validate all proposals for invocation * index varying from '(len - numCharsCompleted)' to 'len'. */ private static void validateExpectedProposals(IJavaProject javaProject, String fullyQualifiedClassName, String source, int lineNum, int numCharsCompleted, String... expectedProposals) throws CoreException, BadLocationException { IProgressMonitor monitor = new NullProgressMonitor(); ICompilationUnit iCompilationUnit = JavaProjectUtilities.createCompilationUnit( javaProject, fullyQualifiedClassName, source); CompilationUnitEditor cuEditor = (CompilationUnitEditor) JavaUI.openInEditor(iCompilationUnit); ISourceViewer viewer = cuEditor.getViewer(); IDocument document = viewer.getDocument(); IRegion lineInformation = document.getLineInformation(lineNum); JsniMethodBodyCompletionProposalComputer jcpc = new JsniMethodBodyCompletionProposalComputer(); for (int numCharsToOverwrite = 0; numCharsToOverwrite <= numCharsCompleted; numCharsToOverwrite++){ int invocationOffset = lineInformation.getOffset() + lineInformation.getLength() - numCharsToOverwrite; JavaContentAssistInvocationContext context = new JavaContentAssistInvocationContext( viewer, invocationOffset, cuEditor); List<ICompletionProposal> completions = jcpc.computeCompletionProposals( context, monitor); int indentationUnits = JsniMethodBodyCompletionProposalComputer.measureIndentationUnits( document, lineNum, lineInformation.getOffset(), javaProject); List<String> expected = createJsniBlocks(javaProject, indentationUnits, expectedProposals); for (int i = 0; i < expected.size(); i++){ String expectedBlock = expected.get(i).substring(numCharsCompleted - numCharsToOverwrite); expected.set(i, expectedBlock); } assertExpectedProposals(expected, completions, numCharsToOverwrite); } } /** * Constructs source for testing JSNI method body completion * with partial braces and comments. An example of source with * comments on both sides and having partial brace is as follow. */ // class A{ // /* Global comment above with brackets () */ // // Single line global comment () // public native int bar()/*- // /* Global comment below with brackets () */ // public void bar1(){ // /* A java method body */ // } // public native void bar2()/*-{ // /* A JSNI method body */ // }-*/; private static int constructPartialBracesSource(String fullyQualifiedClassName, StringBuilder source, int numCharsCompleted, Boolean isCommentAbove, Boolean isCommentBelow) { // invocationLineNum is line number where auto-complete is to be tested. int invocationLineNum = 1; String str = "/*-{".substring(0, numCharsCompleted) + "\n"; source.append("class " + fullyQualifiedClassName + "{\n"); if (isCommentAbove){ source.append(" /* Global comment above with brackets () */\n"); source.append(" // Single line global comment ()\n"); invocationLineNum += 2; } // The line where auto-completion is tested. source.append(" public native int bar()"); source.append(str); if (isCommentBelow){ source.append(" /* Global comment below with brackets () */\n"); } source.append(" public void bar1(){\n"); source.append(" /* A java method body */\n"); source.append(" }\n"); source.append(" public native void bar2()/*-{\n"); source.append(" /* A JSNI method body */\n"); source.append(" }-*/;\n"); source.append("}\n"); return invocationLineNum; } /** * Test that we do not generate any proposals for our boundary conditions. * * @throws CoreException * @throws BadLocationException */ public void testComputeCompletionProposalsNoProposals() throws CoreException, BadLocationException { IProgressMonitor monitor = new NullProgressMonitor(); JsniMethodBodyCompletionProposalComputer jcpc = new JsniMethodBodyCompletionProposalComputer(); IJavaProject javaProject = JavaProjectUtilities.createJavaProject(synthesizeProjectNameForThisTest(this)); StringBuilder source = new StringBuilder(); source.append("class A {\n"); // > 0 proposals if at the signature end. int methodALineNum = 1; source.append(" private native void a()\n"); // No proposals; already has JSNI method body. int methodBLineNum = 2; source.append(" private native void b()/*-{}-*/;\n"); // No proposals; signature ends in a semicolon. int methodCLineNum = 3; source.append(" private native void c();\n"); ICompilationUnit iCompilationUnit = JavaProjectUtilities.createCompilationUnit( javaProject, "A", source.toString()); CompilationUnitEditor cuEditor = (CompilationUnitEditor) JavaUI.openInEditor(iCompilationUnit); ISourceViewer viewer = cuEditor.getViewer(); IDocument document = viewer.getDocument(); // No completions if offset is before the signature. IRegion methodALineInfo = document.getLineInformation(methodALineNum); assertNoProposals(monitor, jcpc, cuEditor, viewer, methodALineInfo.getOffset()); // No completions if the offset is in the middle of method a() signature. assertNoProposals(monitor, jcpc, cuEditor, viewer, methodALineInfo.getOffset() + (methodALineInfo.getLength() / 2)); // > 0 completions at end of the line. assertTrue(jcpc.computeCompletionProposals( new JavaContentAssistInvocationContext(viewer, methodALineInfo.getOffset() + methodALineInfo.getLength(), cuEditor), monitor).size() > 0); // No proposals for method B. IRegion methodBLineInfo = document.getLineInformation(methodBLineNum); assertNoProposals(monitor, jcpc, cuEditor, viewer, methodBLineInfo.getOffset() + methodBLineInfo.getLength()); // No proposals for method C. IRegion methodCLineInfo = document.getLineInformation(methodCLineNum); assertNoProposals(monitor, jcpc, cuEditor, viewer, methodCLineInfo.getOffset() + methodCLineInfo.getLength()); // No proposals for interfaces. StringBuilder sourceI = new StringBuilder(); sourceI.append("interface B {\n"); sourceI.append(" private native void d()\n"); iCompilationUnit = JavaProjectUtilities.createCompilationUnit(javaProject, "B", sourceI.toString()); cuEditor = (CompilationUnitEditor) JavaUI.openInEditor(iCompilationUnit); viewer = cuEditor.getViewer(); document = viewer.getDocument(); int methodDLineNum = 1; IRegion methodDLineInfo = document.getLineInformation(methodDLineNum); // No completions if offset is before the signature. assertNoProposals(monitor, jcpc, cuEditor, viewer, methodDLineInfo.getOffset()); // No completions after the signature either. assertNoProposals(monitor, jcpc, cuEditor, viewer, methodDLineInfo.getOffset() + methodDLineInfo.getLength()); } public void testComputeCompletionsProposalsForGetters() throws CoreException, BadLocationException { IJavaProject javaProject = JavaProjectUtilities.createJavaProject(synthesizeProjectNameForThisTest(this)); StringBuilder aSource = new StringBuilder(); aSource.append("class A {\n"); aSource.append(" private native int getA()\n"); aSource.append("}\n"); validateExpectedProposals(javaProject, "A", aSource.toString(), "", "return this.getA();", "return this.a();", "return this.a;"); StringBuilder aStaticSource = new StringBuilder(); aStaticSource.append("class AStatic {\n"); aStaticSource.append(" private static native int getA()\n"); aStaticSource.append("}\n"); validateExpectedProposals(javaProject, "AStatic", aStaticSource.toString(), "", "return $wnd.getA();", "return $wnd.a();", "return $wnd.a;"); StringBuilder bSource = new StringBuilder(); bSource.append("class B {\n"); bSource.append(" private native int get()\n"); bSource.append("}\n"); validateExpectedProposals(javaProject, "B", bSource.toString(), "", "return this.get();"); StringBuilder bStaticSource = new StringBuilder(); bStaticSource.append("class BStatic {\n"); bStaticSource.append(" private static native int get()\n"); bStaticSource.append("}\n"); validateExpectedProposals(javaProject, "BStatic", bStaticSource.toString(), "", "return $wnd.get();"); } public void testComputeCompletionsProposalsForIndexedGetters() throws CoreException, BadLocationException { IJavaProject javaProject = JavaProjectUtilities.createJavaProject(synthesizeProjectNameForThisTest(this)); StringBuilder aSource = new StringBuilder(); aSource.append("class A {\n"); aSource.append(" private native int getA(int x)\n"); aSource.append("}\n"); validateExpectedProposals(javaProject, "A", aSource.toString(), "", "return this.getA(x);", "return this.a(x);", "return this.a[x];"); StringBuilder aStaticSource = new StringBuilder(); aStaticSource.append("class AStatic {\n"); aStaticSource.append(" private static native int getA(int x)\n"); aStaticSource.append("}\n"); validateExpectedProposals(javaProject, "AStatic", aStaticSource.toString(), "", "return $wnd.getA(x);", "return $wnd.a(x);", "return $wnd.a[x];"); StringBuilder bSource = new StringBuilder(); bSource.append("class B {\n"); bSource.append(" private native int get(int x)\n"); bSource.append("}\n"); validateExpectedProposals(javaProject, "B", bSource.toString(), "", "return this.get(x);", "return this[x];"); StringBuilder bStaticSource = new StringBuilder(); bStaticSource.append("class BStatic {\n"); bStaticSource.append(" private static native int get(int x)\n"); bSource.append("}\n"); validateExpectedProposals(javaProject, "BStatic", bStaticSource.toString(), "", "return $wnd.get(x);", "return $wnd[x];"); } public void testComputeCompletionsProposalsForIndexedSetters() throws CoreException, BadLocationException { IJavaProject javaProject = JavaProjectUtilities.createJavaProject(synthesizeProjectNameForThisTest(this)); StringBuilder aSource = new StringBuilder(); aSource.append("class A {\n"); aSource.append(" private native void setA(int x, int y)\n"); aSource.append("}\n"); validateExpectedProposals(javaProject, "A", aSource.toString(), "", "this.setA(x, y);", "this.a(x, y);", "this.a[x] = y;"); StringBuilder aStaticSource = new StringBuilder(); aStaticSource.append("class AStatic {\n"); aStaticSource.append(" private static native void setA(int x, int y)\n"); aStaticSource.append("}\n"); validateExpectedProposals(javaProject, "AStatic", aStaticSource.toString(), "", "$wnd.setA(x, y);", "$wnd.a(x, y);", "$wnd.a[x] = y;"); StringBuilder bSource = new StringBuilder(); bSource.append("class B {\n"); bSource.append(" private native void set(int x, int y)\n"); bSource.append("}\n"); validateExpectedProposals(javaProject, "B", bSource.toString(), "", "this.set(x, y);", "this[x] = y;"); StringBuilder bStaticSource = new StringBuilder(); bStaticSource.append("class BStatic {\n"); bStaticSource.append(" private static native void set(int x, int y)\n"); bStaticSource.append("}\n"); validateExpectedProposals(javaProject, "BStatic", bStaticSource.toString(), "", "$wnd.set(x, y);", "$wnd[x] = y;"); } public void testComputeCompletionsProposalsForSetters() throws CoreException, BadLocationException { IJavaProject javaProject = JavaProjectUtilities.createJavaProject(synthesizeProjectNameForThisTest(this)); StringBuilder aSource = new StringBuilder(); aSource.append("class A {\n"); aSource.append(" private native void setA(int x)\n"); aSource.append("}\n"); validateExpectedProposals(javaProject, "A", aSource.toString(), "", "this.setA(x);", "this.a(x);", "this.a = x;"); StringBuilder aStaticSource = new StringBuilder(); aStaticSource.append("class AStatic {\n"); aStaticSource.append(" private static native void setA(int x)\n"); aStaticSource.append("}\n"); validateExpectedProposals(javaProject, "AStatic", aStaticSource.toString(), "", "$wnd.setA(x);", "$wnd.a(x);", "$wnd.a = x;"); StringBuilder bSource = new StringBuilder(); bSource.append("class B {\n"); bSource.append(" private native void set(int x)\n"); bSource.append("}\n"); validateExpectedProposals(javaProject, "B", bSource.toString(), "", "this.set(x);"); StringBuilder bStaticSource = new StringBuilder(); bStaticSource.append("class B {\n"); bStaticSource.append(" private static native void set(int x)\n"); bStaticSource.append("}\n"); validateExpectedProposals(javaProject, "BStatic", bStaticSource.toString(), "", "$wnd.set(x);"); } public void testComputeCompletionsProposalsWhenBracesArePresent() throws CoreException, BadLocationException { IJavaProject javaProject = JavaProjectUtilities.createJavaProject(synthesizeProjectNameForThisTest(this)); StringBuilder aSource = new StringBuilder(); aSource.append("class A {\n"); aSource.append(" private native int getA() {\n"); aSource.append("}\n"); validateExpectedProposals(javaProject, "A", aSource.toString()); StringBuilder bSource = new StringBuilder(); bSource.append("class B {\n"); bSource.append(" private native int get() {\n"); bSource.append(" private native someMethod() {\n"); bSource.append(" }\n"); bSource.append("}\n"); validateExpectedProposals(javaProject, "B", bSource.toString()); } public void testComputeCompletionsProposalsWithPartialBraces() throws CoreException, BadLocationException { IJavaProject javaProject = JavaProjectUtilities.createJavaProject(synthesizeProjectNameForThisTest(this)); String className = "A"; for (int numCharsCompleted = 0; numCharsCompleted <= 4; numCharsCompleted++){ StringBuilder source = new StringBuilder(); int invocationLineNum = constructPartialBracesSource(className, source, numCharsCompleted, false, false); validateExpectedProposals(javaProject, className, source.toString(), invocationLineNum, numCharsCompleted, "", "return this.bar();", "return this.bar;"); className = className.concat("A"); } } public void testComputeCompletionsProposalsWithPartialBracesAndCommentsBelow() throws CoreException, BadLocationException { IJavaProject javaProject = JavaProjectUtilities.createJavaProject(synthesizeProjectNameForThisTest(this)); String className = "A"; for (int numCharsCompleted = 0; numCharsCompleted <= 4; numCharsCompleted++){ StringBuilder source = new StringBuilder(); int invocationLineNum = constructPartialBracesSource(className, source, numCharsCompleted, false, true); validateExpectedProposals(javaProject, className, source.toString(), invocationLineNum, numCharsCompleted, "", "return this.bar();", "return this.bar;"); className = className.concat("A"); } } public void testComputeCompletionsProposalsWithPartialBracesAndCommentsAbove() throws CoreException, BadLocationException { IJavaProject javaProject = JavaProjectUtilities.createJavaProject(synthesizeProjectNameForThisTest(this)); String className = "A"; for (int numCharsCompleted = 0; numCharsCompleted <= 4; numCharsCompleted++){ StringBuilder source = new StringBuilder(); int invocationLineNum = constructPartialBracesSource(className, source, numCharsCompleted, true, false); validateExpectedProposals(javaProject, className, source.toString(), invocationLineNum, numCharsCompleted, "", "return this.bar();", "return this.bar;"); className = className.concat("A"); } } public void testComputeCompletionsProposalsWithPartialBracesAndCommentsOnBothSides() throws CoreException, BadLocationException { IJavaProject javaProject = JavaProjectUtilities.createJavaProject(synthesizeProjectNameForThisTest(this)); String className = "A"; for (int numCharsCompleted = 0; numCharsCompleted <= 4; numCharsCompleted++){ StringBuilder source = new StringBuilder(); int invocationLineNum = constructPartialBracesSource(className, source, numCharsCompleted, true, true); validateExpectedProposals(javaProject, className, source.toString(), invocationLineNum, numCharsCompleted, "", "return this.bar();", "return this.bar;"); className = className.concat("A"); } } /** * Ensure that our plugin xml included the correct extension to enable this * {@link JsniMethodBodyCompletionProposalComputer}. */ public void testExtensionPointExistence() { Bundle bundle = GWTPlugin.getDefault().getBundle(); String extensionPointId = "org.eclipse.jdt.ui.javaCompletionProposalComputer"; assertTrue("Plugin " + GWTPlugin.getName() + " did not contribute to extension point " + extensionPointId, BundleUtilities.contributesToExtensionPoint(bundle, JsniMethodBodyCompletionProposalComputer.SIMPLE_EXTENSION_ID, extensionPointId)); } /** * Test method for * {@link JsniMethodBodyCompletionProposalComputer#computePropertyNameFromAccessorMethodName * (java.lang.String, java.lang.String)} */ public void testGetPropertyName() { assertEquals( "foo", JsniMethodBodyCompletionProposalComputer.computePropertyNameFromAccessorMethodName( "is", "isFoo")); assertEquals( "bar", JsniMethodBodyCompletionProposalComputer.computePropertyNameFromAccessorMethodName( "get", "getBar")); } /** * Test that the icons that we depend on are included in the registry. */ public void testIcons() { // TODO: This should really be a test of GWTImages. GWTPlugin plugin = GWTPlugin.getDefault(); ImageRegistry imageRegistry = plugin.getImageRegistry(); for (String imageId : IMAGE_IDS) { Image image = imageRegistry.get(imageId); assertNotNull("ImageId: " + imageId + " was not in the ImageRegistry", image); } } }