blob: 675013fbb19c9075a3738caff6a27519cd5dc43c [file] [log] [blame]
/*******************************************************************************
* 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());
}
}
}