| /******************************************************************************* |
| * Copyright (c) 2000, 2017 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| * Christian Walther (Indel AG) - Bug 399094, 402009: Add whole word option to file search |
| * Terry Parker <tparker@google.com> (Google Inc.) - Bug 441016 - Speed up text search by parallelizing it using JobGroups |
| * Florian Ingerl <imelflorianingerl@gmail.com> - Bug 109481 - [find/replace] replace doesn't work when using a regex with a lookahead or boundary matchers |
| *******************************************************************************/ |
| package org.eclipse.search.tests.filesearch; |
| |
| import static org.junit.Assert.assertEquals; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Scanner; |
| import java.util.regex.Pattern; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.ClassRule; |
| import org.junit.Test; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| |
| import org.eclipse.ui.IWorkbenchPage; |
| |
| import org.eclipse.search.core.text.TextSearchEngine; |
| import org.eclipse.search.core.text.TextSearchMatchAccess; |
| import org.eclipse.search.core.text.TextSearchRequestor; |
| import org.eclipse.search.core.text.TextSearchScope; |
| import org.eclipse.search.internal.core.text.PatternConstructor; |
| import org.eclipse.search.internal.ui.SearchPlugin; |
| import org.eclipse.search.internal.ui.text.FileSearchQuery; |
| import org.eclipse.search.internal.ui.text.FileSearchResult; |
| import org.eclipse.search.internal.ui.text.ReplaceRefactoring; |
| import org.eclipse.search.tests.ResourceHelper; |
| import org.eclipse.search.tests.SearchTestPlugin; |
| import org.eclipse.search.ui.text.FileTextSearchScope; |
| |
| import org.eclipse.ltk.core.refactoring.Change; |
| |
| public class FileSearchTests { |
| |
| private static class TestResult { |
| public IResource resource; |
| public int offset; |
| public int length; |
| |
| public TestResult(IResource resource, int offset, int length) { |
| this.resource= resource; |
| this.offset= offset; |
| this.length= length; |
| } |
| } |
| |
| private static class TestResultCollector extends TextSearchRequestor { |
| |
| protected List<TestResult> fResult; |
| |
| public TestResultCollector() { |
| reset(); |
| } |
| |
| public TestResult[] getResults() { |
| return fResult.toArray(new TestResult[fResult.size()]); |
| } |
| |
| public int getNumberOfResults() { |
| return fResult.size(); |
| } |
| |
| public void reset() { |
| fResult= new ArrayList<>(); |
| } |
| |
| } |
| |
| private static class SerialTestResultCollector extends TestResultCollector { |
| |
| @Override |
| public boolean canRunInParallel() { |
| return false; |
| } |
| |
| @Override |
| public boolean acceptPatternMatch(TextSearchMatchAccess match) throws CoreException { |
| fResult.add(new TestResult(match.getFile(), match.getMatchOffset(), match.getMatchLength())); |
| return true; |
| } |
| |
| } |
| |
| private static class ParallelTestResultCollector extends TestResultCollector { |
| |
| @Override |
| public boolean canRunInParallel() { |
| return true; |
| } |
| |
| @Override |
| public boolean acceptPatternMatch(TextSearchMatchAccess match) throws CoreException { |
| synchronized(fResult) { |
| fResult.add(new TestResult(match.getFile(), match.getMatchOffset(), match.getMatchLength())); |
| } |
| return true; |
| } |
| |
| } |
| |
| @ClassRule |
| public static JUnitSourceSetup fgJUnitSource= new JUnitSourceSetup(); |
| |
| private IProject fProject; |
| |
| @Before |
| public void setUp() throws Exception{ |
| fProject= ResourceHelper.createProject("my-project"); //$NON-NLS-1$ |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| ResourceHelper.deleteProject("my-project"); //$NON-NLS-1$ |
| } |
| |
| @Test |
| public void testSimpleFilesSerial() throws Exception { |
| testSimpleFiles(new SerialTestResultCollector()); |
| } |
| |
| @Test |
| public void testSimpleFilesParallel() throws Exception { |
| testSimpleFiles(new ParallelTestResultCollector()); |
| } |
| |
| private void testSimpleFiles(TestResultCollector collector) throws Exception { |
| StringBuilder buf= new StringBuilder(); |
| buf.append("File1\n"); |
| buf.append("hello\n"); |
| buf.append("more hello\n"); |
| buf.append("world\n"); |
| IFolder folder= ResourceHelper.createFolder(fProject.getFolder("folder1")); |
| IFile file1= ResourceHelper.createFile(folder, "file1", buf.toString()); |
| IFile file2= ResourceHelper.createFile(folder, "file2", buf.toString()); |
| |
| Pattern searchPattern= PatternConstructor.createPattern("hello", false, true); |
| |
| FileTextSearchScope scope= FileTextSearchScope.newSearchScope(new IResource[] {fProject}, (String[]) null, false); |
| TextSearchEngine.create().search(scope, collector, searchPattern, null); |
| |
| TestResult[] results= collector.getResults(); |
| assertEquals("Number of total results", 4, results.length); |
| |
| assertMatches(results, 2, file1, buf.toString(), "hello"); |
| assertMatches(results, 2, file2, buf.toString(), "hello"); |
| } |
| |
| @Test |
| public void testWildCards1Serial() throws Exception { |
| testWildCards1(new SerialTestResultCollector()); |
| } |
| |
| @Test |
| public void testWildCards1Parallel() throws Exception { |
| testWildCards1(new ParallelTestResultCollector()); |
| } |
| |
| private void testWildCards1(TestResultCollector collector) throws Exception { |
| StringBuilder buf= new StringBuilder(); |
| buf.append("File1\n"); |
| buf.append("no more\n"); |
| buf.append("mornings\n"); |
| buf.append("more hello\n"); |
| buf.append("world\n"); |
| IFolder folder= ResourceHelper.createFolder(fProject.getFolder("folder1")); |
| ResourceHelper.createFile(folder, "file1", buf.toString()); |
| ResourceHelper.createFile(folder, "file2", buf.toString()); |
| |
| Pattern searchPattern= PatternConstructor.createPattern("mor*", false, false); |
| |
| FileTextSearchScope scope= FileTextSearchScope.newSearchScope(new IResource[] {fProject}, (String[]) null, false); |
| TextSearchEngine.create().search(scope, collector, searchPattern, null); |
| |
| TestResult[] results= collector.getResults(); |
| assertEquals("Number of total results", 6, results.length); |
| } |
| |
| @Test |
| public void testWildCards2Serial() throws Exception { |
| testWildCards2(new SerialTestResultCollector()); |
| } |
| |
| @Test |
| public void testWildCards2Parallel() throws Exception { |
| testWildCards2(new ParallelTestResultCollector()); |
| } |
| |
| private void testWildCards2(TestResultCollector collector) throws Exception { |
| StringBuilder buf= new StringBuilder(); |
| buf.append("File1\n"); |
| buf.append("no more\n"); |
| buf.append("mornings\n"); |
| buf.append("more hello\n"); |
| buf.append("world\n"); |
| IFolder folder= ResourceHelper.createFolder(fProject.getFolder("folder1")); |
| ResourceHelper.createFile(folder, "file1", buf.toString()); |
| ResourceHelper.createFile(folder, "file2", buf.toString()); |
| |
| Pattern searchPattern= PatternConstructor.createPattern("mo?e", false, false); |
| |
| FileTextSearchScope scope= FileTextSearchScope.newSearchScope(new IResource[] {fProject}, (String[]) null, false); |
| TextSearchEngine.create().search(scope, collector, searchPattern, null); |
| |
| TestResult[] results= collector.getResults(); |
| assertEquals("Number of total results", 4, results.length); |
| } |
| |
| @Test |
| public void testWildCards3Serial() throws Exception { |
| testWildCards3(new SerialTestResultCollector()); |
| } |
| |
| @Test |
| public void testWildCards3Parallel() throws Exception { |
| testWildCards3(new ParallelTestResultCollector()); |
| } |
| |
| private void testWildCards3(TestResultCollector collector) throws Exception { |
| |
| IProject project= fgJUnitSource.getStandardProject(); |
| IFile openFile1= (IFile) project.findMember("junit/framework/TestCase.java"); |
| IFile openFile2= (IFile) project.findMember("junit/extensions/ExceptionTestCase.java"); |
| IFile openFile3= (IFile) project.findMember("junit/framework/Assert.java"); |
| IFile openFile4= (IFile) project.findMember("junit/samples/money/MoneyTest.java"); |
| |
| IWorkbenchPage activePage= SearchPlugin.getActivePage(); |
| try { |
| SearchTestPlugin.openTextEditor(activePage, openFile1); |
| SearchTestPlugin.openTextEditor(activePage, openFile2); |
| SearchTestPlugin.openTextEditor(activePage, openFile3); |
| SearchTestPlugin.openTextEditor(activePage, openFile4); |
| |
| long start= System.currentTimeMillis(); |
| |
| Pattern searchPattern= PatternConstructor.createPattern("\\w*\\(\\)", false, true); |
| |
| // search in Junit sources |
| |
| FileTextSearchScope scope= FileTextSearchScope.newSearchScope(new IResource[] {project}, (String[]) null, false); |
| TextSearchEngine.create().search(scope, collector, searchPattern, null); |
| |
| TestResult[] results= collector.getResults(); |
| assertEquals("Number of total results", 748, results.length); |
| long end= System.currentTimeMillis(); |
| System.out.println("time= " + (end - start)); |
| } finally { |
| activePage.closeAllEditors(false); |
| } |
| |
| } |
| |
| @Test |
| public void testWholeWordSerial() throws Exception { |
| testWholeWord(new SerialTestResultCollector()); |
| } |
| |
| @Test |
| public void testWholeWordParallel() throws Exception { |
| testWholeWord(new ParallelTestResultCollector()); |
| } |
| |
| private void testWholeWord(TestResultCollector collector) throws Exception { |
| StringBuilder buf= new StringBuilder(); |
| // nothing after |
| buf.append("hell\n"); // nothing before |
| buf.append("hill\n"); // nothing before |
| buf.append("$hell\n"); // non-word char before |
| buf.append("shell\n"); // word char before |
| // non-word char after |
| buf.append("hell.freeze()\n"); // nothing before |
| buf.append("freeze(hell)\n"); // non-word char before |
| buf.append("shell-script\n"); // word char before |
| // word char after |
| buf.append("hello\n"); // nothing before |
| buf.append("world.hello()\n"); // non-word char before |
| buf.append("shilling\n"); // word char before |
| buf.append("holler\n"); // nothing before |
| IFolder folder= ResourceHelper.createFolder(fProject.getFolder("folder1")); |
| ResourceHelper.createFile(folder, "file1", buf.toString()); |
| ResourceHelper.createFile(folder, "file2", buf.toString()); |
| |
| TextSearchEngine engine= TextSearchEngine.create(); |
| FileTextSearchScope scope= FileTextSearchScope.newSearchScope(new IResource[] { fProject }, (String[])null, false); |
| |
| { |
| // wildcards, whole word = false: match all lines |
| Pattern searchPattern= PatternConstructor.createPattern("h?ll", false, true, false, false); |
| collector.reset(); |
| engine.search(scope, collector, searchPattern, null); |
| assertEquals("Number of partial-word results", 22, collector.getNumberOfResults()); |
| } |
| { |
| // wildcards, whole word = true: match only nothing and non-word chars before and after |
| Pattern searchPattern= PatternConstructor.createPattern("h?ll", false, true, false, true); |
| collector.reset(); |
| engine.search(scope, collector, searchPattern, null); |
| assertEquals("Number of whole-word results", 10, collector.getNumberOfResults()); |
| } |
| { |
| // regexp, whole word = false: match all lines |
| Pattern searchPattern= PatternConstructor.createPattern("h[eio]ll", true, true, false, false); |
| collector.reset(); |
| engine.search(scope, collector, searchPattern, null); |
| assertEquals("Number of partial-word results", 22, collector.getNumberOfResults()); |
| } |
| } |
| |
| @Test |
| public void testFileOpenInEditorSerial() throws Exception { |
| testFileOpenInEditor(new SerialTestResultCollector()); |
| } |
| |
| @Test |
| public void testFileOpenInEditorParallel() throws Exception { |
| testFileOpenInEditor(new ParallelTestResultCollector()); |
| } |
| |
| private void testFileOpenInEditor(TestResultCollector collector) throws Exception { |
| StringBuilder buf= new StringBuilder(); |
| buf.append("File1\n"); |
| buf.append("hello\n"); |
| buf.append("more hello\n"); |
| buf.append("world\n"); |
| IFolder folder= ResourceHelper.createFolder(fProject.getFolder("folder1")); |
| IFile file1= ResourceHelper.createFile(folder, "file1", buf.toString()); |
| IFile file2= ResourceHelper.createFile(folder, "file2", buf.toString()); |
| |
| try { |
| SearchTestPlugin.openTextEditor(SearchPlugin.getActivePage(), file2); |
| |
| Pattern searchPattern= PatternConstructor.createPattern("hello", false, true); |
| |
| FileTextSearchScope scope= FileTextSearchScope.newSearchScope(new IResource[] {fProject}, (String[]) null, false); |
| TextSearchEngine.create().search(scope, collector, searchPattern, null); |
| |
| TestResult[] results= collector.getResults(); |
| assertEquals("Number of total results", 4, results.length); |
| |
| assertMatches(results, 2, file1, buf.toString(), "hello"); |
| assertMatches(results, 2, file2, buf.toString(), "hello"); |
| } finally { |
| SearchPlugin.getActivePage().closeAllEditors(false); |
| } |
| } |
| |
| @Test |
| public void testDerivedFilesSerial() throws Exception { |
| testDerivedFiles(new SerialTestResultCollector()); |
| } |
| |
| @Test |
| public void testDerivedFilesParallel() throws Exception { |
| testDerivedFiles(new ParallelTestResultCollector()); |
| } |
| |
| private void testDerivedFiles(TestResultCollector collector) throws Exception { |
| StringBuilder buf= new StringBuilder(); |
| buf.append("hello\n"); |
| IFolder folder1= ResourceHelper.createFolder(fProject.getFolder("folder1")); |
| ResourceHelper.createFile(folder1, "file1", buf.toString()); |
| IFile file2= ResourceHelper.createFile(folder1, "file2", buf.toString()); |
| file2.setDerived(true, null); |
| |
| IFolder folder2= ResourceHelper.createFolder(folder1.getFolder("folder2")); |
| folder2.setDerived(true, null); |
| ResourceHelper.createFile(folder2, "file3", buf.toString()); |
| |
| IFolder folder3= ResourceHelper.createFolder(folder2.getFolder("folder3")); |
| ResourceHelper.createFile(folder3, "file4", buf.toString()); |
| |
| IFolder folder4= ResourceHelper.createFolder(folder1.getFolder("folder4")); |
| ResourceHelper.createFile(folder4, "file5", buf.toString()); |
| |
| /** |
| * folder1 |
| * file1 |
| * file2* |
| * folder2* |
| * file3 |
| * folder3 |
| * file4 |
| * folder4 |
| * file5 |
| */ |
| |
| |
| Pattern searchPattern= PatternConstructor.createPattern("hello", false, true); |
| Pattern fileNamePattern= PatternConstructor.createPattern("*", false, false); |
| TextSearchEngine engine= TextSearchEngine.create(); |
| { |
| // visit all |
| TextSearchScope scope= TextSearchScope.newSearchScope(new IResource[] { fProject }, fileNamePattern, true); |
| collector.reset(); |
| engine.search(scope, collector, searchPattern, null); |
| assertEquals(5, collector.getNumberOfResults()); |
| } |
| { |
| // visit non-derived |
| TextSearchScope scope= TextSearchScope.newSearchScope(new IResource[] { fProject }, fileNamePattern, false); |
| collector.reset(); |
| engine.search(scope, collector, searchPattern, null); |
| assertEquals(2, collector.getNumberOfResults()); |
| } |
| { |
| // visit all in folder2 |
| TextSearchScope scope= TextSearchScope.newSearchScope(new IResource[] { folder2 }, fileNamePattern, true); |
| collector.reset(); |
| engine.search(scope, collector, searchPattern, null); |
| assertEquals(2, collector.getNumberOfResults()); |
| } |
| { |
| // visit non-derived in folder2 |
| TextSearchScope scope= TextSearchScope.newSearchScope(new IResource[] { folder2 }, fileNamePattern, false); |
| collector.reset(); |
| engine.search(scope, collector, searchPattern, null); |
| assertEquals(0, collector.getNumberOfResults()); |
| } |
| { |
| // visit all in folder3 |
| TextSearchScope scope= TextSearchScope.newSearchScope(new IResource[] { folder3 }, fileNamePattern, true); |
| collector.reset(); |
| engine.search(scope, collector, searchPattern, null); |
| assertEquals(1, collector.getNumberOfResults()); |
| } |
| { |
| // visit non-derived in folder3 |
| TextSearchScope scope= TextSearchScope.newSearchScope(new IResource[] { folder3 }, fileNamePattern, false); |
| collector.reset(); |
| engine.search(scope, collector, searchPattern, null); |
| assertEquals(0, collector.getNumberOfResults()); |
| } |
| } |
| |
| @Test |
| public void testFileNamePatternsSerial() throws Exception { |
| testFileNamePatterns(new SerialTestResultCollector()); |
| } |
| |
| @Test |
| public void testFileNamePatternsParallel() throws Exception { |
| testFileNamePatterns(new ParallelTestResultCollector()); |
| } |
| |
| |
| private void testFileNamePatterns(TestResultCollector collector) throws Exception { |
| IFolder folder= ResourceHelper.createFolder(fProject.getFolder("folder1")); |
| ResourceHelper.createFile(folder, "file1.x", "Test"); |
| ResourceHelper.createFile(folder, "file2.x", "Test"); |
| ResourceHelper.createFile(folder, "file2.y", "Test"); |
| ResourceHelper.createFile(folder, "file2.z", "Test"); |
| |
| Pattern searchPattern= PatternConstructor.createPattern("Test", false, false); |
| String[] fileNamePatterns= { "*" }; |
| |
| TestResult[] results= performSearch(collector, fileNamePatterns, searchPattern); |
| assertEquals("Number of total results", 4, results.length); |
| |
| fileNamePatterns= new String[] { "*.x" }; |
| results= performSearch(collector, fileNamePatterns, searchPattern); |
| assertEquals("Number of total results", 2, results.length); |
| |
| fileNamePatterns= new String[] { "*.x", "*.y*" }; |
| results= performSearch(collector, fileNamePatterns, searchPattern); |
| assertEquals("Number of total results", 3, results.length); |
| |
| fileNamePatterns= new String[] { "!*.x" }; |
| results= performSearch(collector, fileNamePatterns, searchPattern); |
| assertEquals("Number of total results", 2, results.length); |
| |
| fileNamePatterns= new String[] { "!*.x", "!*.y" }; |
| results= performSearch(collector, fileNamePatterns, searchPattern); |
| assertEquals("Number of total results", 1, results.length); |
| |
| fileNamePatterns= new String[] { "*", "!*.y" }; |
| results= performSearch(collector, fileNamePatterns, searchPattern); |
| assertEquals("Number of total results", 3, results.length); |
| |
| fileNamePatterns= new String[] { "*", "!*.*" }; |
| results= performSearch(collector, fileNamePatterns, searchPattern); |
| assertEquals("Number of total results", 0, results.length); |
| |
| fileNamePatterns= new String[] { "*.x", "*.y*", "!*.y" }; |
| results= performSearch(collector, fileNamePatterns, searchPattern); |
| assertEquals("Number of total results", 2, results.length); |
| |
| fileNamePatterns= new String[] { "file*", "!*.x*", "!*.y" }; |
| results= performSearch(collector, fileNamePatterns, searchPattern); |
| assertEquals("Number of total results", 1, results.length); |
| } |
| |
| private TestResult[] performSearch(TestResultCollector collector, String[] fileNamePatterns, Pattern searchPattern) { |
| collector.reset(); |
| FileTextSearchScope scope= FileTextSearchScope.newSearchScope(new IResource[] {fProject}, fileNamePatterns, false); |
| TextSearchEngine.create().search(scope, collector, searchPattern, null); |
| |
| return collector.getResults(); |
| |
| } |
| |
| |
| |
| private void assertMatches(TestResult[] results, int expectedCount, IFile file, String fileContent, String string) { |
| int k= 0; |
| for (int i= 0; i < results.length; i++) { |
| TestResult curr= results[i]; |
| if (file.equals(curr.resource)) { |
| k++; |
| assertEquals("Wrong positions", string, fileContent.substring(curr.offset, curr.offset + curr.length)); |
| } |
| } |
| assertEquals("Number of results in file", expectedCount, k); |
| } |
| |
| @Test |
| public void testReplaceWithLookarounds() throws CoreException, IOException { |
| IFolder folder= ResourceHelper.createFolder(fProject.getFolder("folder1")); |
| IFile file1= ResourceHelper.createFile(folder, "file1.txt", "1<2<3<4"); |
| IFile file2= ResourceHelper.createFile(folder, "file2.txt", "4<5<6<7"); |
| |
| FileSearchResult searchResult= performSearch(new String[] { "*.txt" }, "(?<=(\\d)\\<)\\d(?=\\<(\\d))"); |
| performReplace(searchResult, "$0=($1+$2)/2"); |
| |
| assertFileContent(file1, "1<2=(1+3)/2<3=(2+4)/2<4"); |
| assertFileContent(file2, "4<5=(4+6)/2<6=(5+7)/2<7"); |
| } |
| |
| @Test |
| public void testReplaceRetainCase() throws CoreException, IOException { |
| IFolder folder= ResourceHelper.createFolder(fProject.getFolder("folder1")); |
| IFile file1= ResourceHelper.createFile(folder, "file1.txt", "FOO"); |
| |
| FileSearchResult searchResult= performSearch(new String[] { "*.txt" }, "FOO"); |
| performReplace(searchResult, "xyz\\Cbar\\Cfar"); |
| |
| assertFileContent(file1, "xyzBARFAR"); |
| } |
| |
| private FileSearchResult performSearch(String[] fileNamePatterns, String pattern) { |
| FileTextSearchScope scope= FileTextSearchScope.newSearchScope(new IResource[] { fProject }, fileNamePatterns, false); |
| FileSearchQuery query= new FileSearchQuery(pattern, true, true, scope); |
| query.run(null); |
| return (FileSearchResult) query.getSearchResult(); |
| } |
| |
| private void performReplace(FileSearchResult searchResult, String replacementText) throws OperationCanceledException, CoreException { |
| ReplaceRefactoring refactoring= new ReplaceRefactoring(searchResult, null); |
| refactoring.setReplaceString(replacementText); |
| refactoring.checkInitialConditions(null); |
| refactoring.checkFinalConditions(null); |
| Change change= refactoring.createChange(null); |
| change.perform(new NullProgressMonitor()); |
| } |
| |
| private void assertFileContent(IFile file, String expected) throws CoreException { |
| try (Scanner scanner= new Scanner(file.getContents())) { |
| scanner.useDelimiter("\\A"); |
| assertEquals(expected, scanner.next()); |
| } |
| } |
| } |