/* * Copyright 2009-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 org.eclipse.jdt.core.groovy.tests.search; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchParticipant; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.SearchRequestor; import org.junit.Test; /** * Tests the groovy-specific type referencing search support. */ public final class TypeReferenceSearchTests extends SearchTestSuite { @Test public void testSearchForTypesScript1() throws Exception { doTestForTwoInScript("First f = new First()"); } @Test public void testSearchForTypesScript2() throws Exception { doTestForTwoInScript("First.class\nFirst.class"); } @Test public void testSearchForTypesScript3() throws Exception { doTestForTwoInScript("[First, First]"); } @Test public void testSearchForTypesScript4() throws Exception { // the key of a map is interpreted as a string // so don't put 'First' there doTestForTwoInScript("def x = [a : First]\nx[First.class]"); } @Test public void testSearchForTypesScript5() throws Exception { doTestForTwoInScript("x(First, First.class)"); } @Test public void testSearchForTypesScript6() throws Exception { // note that in "new First[ new First() ]", the first 'new First' is removed // by the AntlrPluginParser and so there is no way to search for it // doTestForTwoInScript("new First[ new First() ]"); doTestForTwoInScript("[ new First(), First ]"); } @Test public void testSearchForTypesClosure1() throws Exception { doTestForTwoInScript("{ First first, First second -> print first; print second;}"); } @Test public void testSearchForTypesClosure2() throws Exception { doTestForTwoInScript("def x = { First first = new First() }"); } @Test public void testSearchForTypesClosure3() throws Exception { doTestForTwoInScript("def x = { First.class\n First.class }"); } @Test public void testSearchForTypesClass1() throws Exception { doTestForTwoInClass("class Second extends First { First x }"); } @Test public void testSearchForTypesClass2() throws Exception { doTestForTwoInClass("class Second extends First { First x() { } }"); } @Test public void testSearchForTypesClass3() throws Exception { doTestForTwoInClass("class Second extends First { def x(First y) { } }"); } @Test public void testSearchForTypesClass4() throws Exception { doTestForTwoInClass("class Second extends First { def x(First ... y) { } }"); } @Test public void testSearchForTypesClass5() throws Exception { doTestForTwoInClassUseWithDefaultMethod("class Second extends First { def x(y = new First()) { } }"); } @Test public void testSearchForTypesClass6() throws Exception { doTestForTwoInClassWithImplements("class Second implements First { def x(First y) { } }"); } @Test public void testSearchForTypesClass7() throws Exception { createUnit("other", "First", "class First { }"); doTestForTwoInClass("class Second extends First {\n" + " def x() {\n" + // yes " y = new other.First()\n" + // no " y = new First()} }"); // yes } @Test public void testSearchForTypesArray1() throws Exception { doTestForTwoInScript("First[] f = { First[] h -> h }"); } @Test // GRECLIPSE-650 public void testFindClassDeclaration() throws Exception { String firstContents = "class First { First x }"; String secondContents = "class Second extends First {}"; List<SearchMatch> matches = getAllMatches(firstContents, secondContents); assertEquals("Should find First 2 times", 2, matches.size()); SearchMatch match = matches.get(0); int start = match.getOffset(); int end = start + match.getLength(); assertEquals("Invalid location", "First", firstContents.substring(start, end)); match = matches.get(1); start = match.getOffset(); end = start + match.getLength(); assertEquals("Invalid location", "First", secondContents.substring(start, end)); } /** * Tests whether queries looking for some type declaration with a name pattern like '*Tests' works * correctly. */ @Test public void testFindClassDeclarationWithPattern() throws Exception { //Code in here directly inspired and mostly copied from //com.springsource.sts.grails.core.junit.Grails20AwareTestFinder //Specifically exercises the exact kind of searching behavior needed to find //Grails 2.0 tests that do *not* have @TestFor annotations. int matchRule= SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE; SearchPattern testPattern= SearchPattern.createPattern("*Tests", IJavaSearchConstants.TYPE, IJavaSearchConstants.DECLARATIONS, matchRule); GroovyCompilationUnit songTests = createUnit("gtunes", "SongTests", "package gtunes\n" + "\n" + "class SongTests {" + " def testSomething() {\n" + " println 'testing'\n" + " }\n" + "}"); GroovyCompilationUnit weirdTests = createUnit("gtunes", "Song2tests", "package gtunes\n" + "\n" + "class Song2tests {" + " SongTests theOtherTests\n"+ //Shouldn't find " def testSomethingElse() {\n" + " println 'testing'\n" + " }\n" + "}"); GroovyCompilationUnit artistTests = createUnit("gtunes", "ArtistTests", "package gtunes\n" + "\n" + "class ArtistTests {" + " def testSomething() {\n" + " println 'testing'\n" + " }\n" + "}"); IJavaProject javaProject = JavaCore.create(project); IType songTestsType = javaProject.findType("gtunes.SongTests"); assertNotNull(songTestsType); IType artistTestsType = javaProject.findType("gtunes.ArtistTests"); assertNotNull(artistTestsType); SearchParticipant[] searchParticipants = new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }; final ArrayList<Object> result = new ArrayList<Object>(); IJavaSearchScope scope= SearchEngine.createJavaSearchScope( new IJavaElement[] { songTests, weirdTests, artistTests}, IJavaSearchScope.SOURCES); SearchRequestor requestor= new SearchRequestor() { @Override public void acceptSearchMatch(SearchMatch match) throws CoreException { Object element= match.getElement(); result.add(element); } }; new SearchEngine().search(testPattern, searchParticipants, scope, requestor, new NullProgressMonitor()); assertEquals("Number of results found", 2, result.size()); assertElements(new HashSet<Object>(result), songTestsType, artistTestsType); } @Test // GRECLIPSE-628 public void testShouldntFindClassDeclarationInScript() throws Exception { String firstContents = "print 'me'"; String secondContents = "print 'me'"; List<SearchMatch> matches = getAllMatches(firstContents, secondContents); assertEquals("Should find no matches", 0, matches.size()); } @Test public void testInnerTypes1() throws Exception { String firstContents = "class Other {\n" + " class First { }\n" + "}"; String secondContents = "import Other.First\n" + "Map<First, ? extends First> h\n" + "Other.First j\n" + "First i"; String name = "First"; int len = name.length(); List<SearchMatch> matches = getAllMatches(firstContents, secondContents, true); assertEquals("Wrong number of matches found:\n" + matches, 5, matches.size()); int start = secondContents.indexOf("First"); SearchMatch match = matches.get(0); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); start = secondContents.indexOf("First", start+1); match = matches.get(1); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); start = secondContents.indexOf("First", start+1); match = matches.get(2); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); start = secondContents.indexOf("First", start+1); match = matches.get(3); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); start = secondContents.indexOf("First", start+1); match = matches.get(4); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); } @Test public void testInnerTypes2() throws Exception { String firstContents = "package p\n" + "class Other {\n" + " class First { }\n" + "}"; String secondContents = "package q\n" + "import p.Other.First\n" + "Map<First, ? extends First> h\n" + "p.Other.First j\n" + "First i"; String name = "First"; int len = name.length(); List<SearchMatch> matches = getAllMatches(firstContents, secondContents, "p", "q", true); assertEquals("Wrong number of matches found:\n" + matches, 5, matches.size()); int start = secondContents.indexOf("First"); SearchMatch match = matches.get(0); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); start = secondContents.indexOf("First", start+1); match = matches.get(1); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); start = secondContents.indexOf("First", start+1); match = matches.get(2); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); start = secondContents.indexOf("First", start+1); match = matches.get(3); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); start = secondContents.indexOf("First", start+1); match = matches.get(4); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); } @Test public void testInnerTypes3() throws Exception { String firstContents = "package p\n" + "class Other {\n" + " class First { }\n" + "}"; String secondContents = "package q\n" + "import p.Other\n" + "Map<Other.First, ? extends Other.First> h\n" + "p.Other.First j\n" + "Other.First i"; String name = "First"; int len = name.length(); List<SearchMatch> matches = getAllMatches(firstContents, secondContents, "p", "q", true); assertEquals("Wrong number of matches found:\n" + matches, 4, matches.size()); int start = secondContents.indexOf("First"); SearchMatch match = matches.get(0); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); start = secondContents.indexOf("First", start+1); match = matches.get(1); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); start = secondContents.indexOf("First", start+1); match = matches.get(2); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); start = secondContents.indexOf("First", start+1); match = matches.get(3); assertEquals("Wrong offset " + match, start, match.getOffset()); assertEquals("Wrong length " + match, len, match.getLength()); } @Test public void testConstructorWithDefaultArgsInCompileStatic() throws Exception { String firstContents = "package p\n" + "class First {\n" + " Class name\n" + "}\n"; String secondContents = "package q\n" + "import p.First\n" + "import groovy.transform.CompileStatic\n" + "\n" + "@CompileStatic\n" + "class Foo {\n" + " void apply() {\n" + " new First([name: First])\n" + " }\n" + "}"; List<SearchMatch> matches = getAllMatches(firstContents, secondContents, "p", "q", true); int lastMatch = 0; for (SearchMatch searchMatch : matches) { int start = secondContents.indexOf("First", lastMatch); assertEquals("Wrong offset " + searchMatch, start, searchMatch.getOffset()); assertEquals("Wrong length " + searchMatch, "First".length(), searchMatch.getLength()); lastMatch = start+1; } assertEquals("Wrong number of matches found\n" + matches, 3, matches.size()); } //-------------------------------------------------------------------------- private static void assertElements(Set<Object> actualSet, Object... expecteds) { Set<Object> expectedSet = new HashSet<Object>(Arrays.asList(expecteds)); StringBuilder msg = new StringBuilder(); for (Object expected : expectedSet) { if (!actualSet.contains(expected)) { msg.append("Expected but not found: "+expected+"\n"); } } for (Object actual : actualSet) { if (!expectedSet.contains(actual)) { msg.append("Found but not expected: "+actual+"\n"); } } if (!"".equals(msg.toString())) { fail(msg.toString()); } } private void doTestForTwoInScript(String secondContents) throws Exception { doTestForTwoTypeReferences(FIRST_CONTENTS_CLASS, secondContents, true, 3); } private void doTestForTwoInClass(String secondContents) throws Exception { doTestForTwoTypeReferences(FIRST_CONTENTS_CLASS, secondContents, false, 0); } private void doTestForTwoInClassUseWithDefaultMethod(String secondContents) throws Exception { // capture the default method that is created instead of the original method // it seems that the order of the method variants is switched depending on whether or not // concrete asts are requested. doTestForTwoTypeReferences(FIRST_CONTENTS_CLASS, secondContents, false, 1); } private void doTestForTwoInClassWithImplements(String secondContents) throws Exception { doTestForTwoTypeReferences(FIRST_CONTENTS_INTERFACE, secondContents, false, 0); } }