blob: 14b10991fcdac6329cc3a63b85ac8c9d2fba9b8e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2015 Symbian Software Systems 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:
* Andrew Ferguson (Symbian) - Initial implementation
* IBM Corporation
* Markus Schorn (Wind River Systems)
* Sergey Prigogin (Google)
* Nathan Ridge
*******************************************************************************/
package org.eclipse.cdt.internal.index.tests;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IPDOMManager;
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
import org.eclipse.cdt.core.dom.ast.IPointerType;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.parser.tests.ast2.SemanticTestBase;
import org.eclipse.cdt.core.testplugin.CProjectHelper;
import org.eclipse.cdt.core.testplugin.CTestPlugin;
import org.eclipse.cdt.core.testplugin.TestScannerProvider;
import org.eclipse.cdt.core.testplugin.util.TestSourceReader;
import org.eclipse.cdt.internal.core.CCoreInternals;
import org.eclipse.cdt.internal.core.dom.parser.ITypeContainer;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTNameBase;
import org.eclipse.cdt.internal.core.pdom.PDOM;
import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences;
import org.eclipse.cdt.internal.pdom.tests.PDOMPrettyPrinter;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.osgi.framework.Bundle;
/**
* When the PDOM is used to avoid parsing work (i.e. an AST is obtained which
* is backed by the PDOM), it must be possible to resolve which binding a name
* in the AST is referring to. If the binding is not defined in the AST fragment
* then it is assumed to have come from a file which is already indexed.
*
* This class is for testing the process by which bindings are looked up in
* the PDOM purely from AST information (i.e. without a real binding from the DOM).
*/
public abstract class IndexBindingResolutionTestBase extends SemanticTestBase {
private static final boolean DEBUG= false;
private static final String END_OF_ADDED_CODE_MARKER = "/*END_OF_ADDED_CODE*/";
protected ITestStrategy strategy;
public void setStrategy(ITestStrategy strategy) {
this.strategy = strategy;
}
@Override
protected void setUp() throws Exception {
super.setUp();
strategy.setUp();
}
@Override
protected void tearDown() throws Exception {
strategy.tearDown();
super.tearDown();
}
protected IASTName findName(String section, int offset, int len,
boolean preferImplicitName) {
if (len <= 0)
len += section.length();
for (int i = 0; i < strategy.getAstCount(); i++) {
IASTTranslationUnit ast = strategy.getAst(i);
final IASTNodeSelector nodeSelector = ast.getNodeSelector(null);
String source = ast.getRawSignature();
// Skip the automatically added code.
int sectionOffset = source.indexOf(END_OF_ADDED_CODE_MARKER);
if (sectionOffset >= 0) {
sectionOffset += END_OF_ADDED_CODE_MARKER.length();
if (source.charAt(sectionOffset) == '\n')
sectionOffset++;
} else {
sectionOffset = 0;
}
sectionOffset = source.indexOf(section, sectionOffset);
if (sectionOffset >= 0) {
IASTName name= null;
if (!preferImplicitName)
name= nodeSelector.findName(sectionOffset + offset, len);
if (name == null)
name= nodeSelector.findImplicitName(sectionOffset + offset, len);
return name;
}
}
return null;
}
protected IASTName findName(String section, int offset, int len) {
return findName(section, offset, len, false);
}
protected IASTName findName(String section, int len) {
return findName(section, 0, len);
}
protected IASTName findImplicitName(String section, int offset, int len) {
return findName(section, offset, len, true);
}
protected IASTName findImplicitName(String section, int len) {
return findName(section, 0, len);
}
/**
* Attempts to get an IBinding from the initial specified number of characters
* from the specified code fragment. Fails the test if
* <ul>
* <li> There is not a unique name with the specified criteria
* <li> The binding associated with the name is null or a problem binding
* <li> The binding is not an instance of the specified class
* </ul>
* @param section the code fragment to search for in the AST. The first occurrence of an identical
* section is used.
* @param offset the offset of the name within the section
* @param len the length of the name. This can also be useful for distinguishing between template names
* and template ids.
* @param clazz an expected class type or interface that the binding should extend/implement
* @return the associated name's binding
*/
protected <T> T getBindingFromASTName(String section, int offset, int len, Class<T> clazz, Class... cs) {
if (len <= 0)
len += section.length() - offset;
IASTName name= findName(section, offset, len);
assertNotNull("Name not found for \"" + section + "\"", name);
assertEquals(section.substring(0, len), name.getRawSignature());
IBinding binding = name.resolveBinding();
assertNotNull("No binding for " + name.getRawSignature(), binding);
assertFalse("Binding is a ProblemBinding for name \"" + name.getRawSignature() + "\"", IProblemBinding.class.isAssignableFrom(name.resolveBinding().getClass()));
assertInstance(binding, clazz, cs);
return clazz.cast(binding);
}
protected <T> T getBindingFromASTName(String section, int len, Class<T> clazz, Class... cs) {
return getBindingFromASTName(section, 0, len, clazz, cs);
}
/**
* Attempts to get an IBinding attached to an implicit name from the initial specified
* number of characters from the specified code fragment. Fails the test if
* <ul>
* <li> There is not a unique implicit name with the specified criteria
* <li> The binding associated with the implicit name is null or a problem binding
* <li> The binding is not an instance of the specified class
* </ul>
* @param section the code fragment to search for in the AST. The first occurrence of an identical
* section is used.
* @param offset the offset of the name within the section
* @param len the length of the name
* @param clazz an expected class type or interface that the binding should extend/implement
* @return the associated implicit name's binding
*/
protected <T> T getBindingFromImplicitASTName(String section, int offset, int len, Class<T> clazz, Class... cs) {
if (len <= 0)
len += section.length() - offset;
IASTName name= findImplicitName(section, offset, len);
assertNotNull("Name not found for \"" + section + "\"", name);
assertEquals(section.substring(offset, offset + len), name.getRawSignature());
IBinding binding = name.resolveBinding();
assertNotNull("No binding for " + name.getRawSignature(), binding);
assertFalse("Binding is a ProblemBinding for name \"" + name.getRawSignature() + "\"", IProblemBinding.class.isAssignableFrom(name.resolveBinding().getClass()));
assertInstance(binding, clazz, cs);
return clazz.cast(binding);
}
protected <T> T getBindingFromImplicitASTName(String section, int len, Class<T> clazz, Class... cs) {
return getBindingFromImplicitASTName(section, 0, len, clazz, cs);
}
/**
* @see IndexBindingResolutionTestBase#getBindingFromASTName(String, int, Class<T>, Class...)
*/
protected <T extends IBinding> T getBindingFromASTName(String section, int len) {
return getBindingFromASTName(section, 0, len);
}
/**
* @see IndexBindingResolutionTestBase#getBindingFromASTName(String, int, Class<T>, Class...)
*/
protected <T extends IBinding> T getBindingFromASTName(String section, int offset, int len) {
if (len <= 0)
len += section.length() - offset;
IASTName name= findName(section, offset, len);
assertNotNull("Name not found for \"" + section + "\"", name);
assertEquals(section.substring(offset, offset + len), name.getRawSignature());
IBinding binding = name.resolveBinding();
assertNotNull("No binding for " + name.getRawSignature(), binding);
assertFalse("Binding is a ProblemBinding for name \"" + name.getRawSignature() + "\"", IProblemBinding.class.isAssignableFrom(name.resolveBinding().getClass()));
return (T) binding;
}
protected <T extends IBinding> T getBindingFromFirstIdentifier(String section) {
int offset = getIdentifierOffset(section);
return getBindingFromASTName(section, offset, getIdentifierLength(section, offset));
}
protected <T extends IBinding> T getBindingFromFirstIdentifier(String section, Class<T> clazz, Class... cs) {
int offset = getIdentifierOffset(section);
return getBindingFromASTName(section, offset, getIdentifierLength(section, offset), clazz, cs);
}
/*
* @see IndexBindingResolutionTestBase#getBindingFromImplicitASTName(String, int, Class<T>, Class ...)
*/
protected <T extends IBinding> T getBindingFromImplicitASTName(String section, int len) {
if (len <= 0)
len += section.length();
IASTName name= findImplicitName(section, 0, len);
assertNotNull("Name not found for \"" + section + "\"", name);
assertEquals(section.substring(0, len), name.getRawSignature());
IBinding binding = name.resolveBinding();
assertNotNull("No binding for " + name.getRawSignature(), binding);
assertFalse("Binding is a ProblemBinding for name \"" + name.getRawSignature() + "\"",
IProblemBinding.class.isAssignableFrom(name.resolveBinding().getClass()));
return (T) binding;
}
/**
* Attempts to verify that the resolved binding for a name is a problem binding.
* @param section the code fragment to search for in the AST. The first occurrence of an identical section is used.
* @param len the length of the specified section to use as a name
* @return the associated name's binding
*/
protected IBinding getProblemFromASTName(String section, int len) {
IASTName name= findName(section, 0, len);
assertNotNull("Name not found for \"" + section + "\"", name);
assertEquals(section.substring(0, len), name.getRawSignature());
IBinding binding = name.resolveBinding();
assertNotNull("No binding for " + name.getRawSignature(), binding);
assertTrue("Binding is not a ProblemBinding for name \"" + name.getRawSignature() + "\"",
IProblemBinding.class.isAssignableFrom(name.resolveBinding().getClass()));
return name.resolveBinding();
}
protected IBinding getProblemFromFirstIdentifier(String section) {
int offset = getIdentifierOffset(section);
return getProblemFromASTName(section, getIdentifierLength(section, offset));
}
protected static void assertQNEquals(String expectedQN, IBinding b) {
assertInstance(b, IBinding.class);
if (b instanceof ICPPBinding) {
assertEquals(expectedQN, ASTTypeUtil.getQualifiedName((ICPPBinding) b));
} else {
assertEquals(expectedQN, b.getName());
}
}
protected IType getVariableType(IBinding binding) throws DOMException {
assertTrue(binding instanceof IVariable);
return ((IVariable) binding).getType();
}
protected IType getPtrType(IBinding binding) throws DOMException {
// assert binding is a variable
IVariable v = (IVariable) binding;
IPointerType ptr = (IPointerType) v.getType();
return ptr.getType();
}
protected void assertParamType(int index, Class type, IType function) throws DOMException {
// assert function is IFunctionType
IFunctionType ft = (IFunctionType) function;
assertTrue(type.isInstance((ft.getParameterTypes()[index])));
}
protected void assertCompositeTypeParam(int index, int compositeTypeKey, IType function, String qn) throws DOMException {
// assert function is IFunctionType
IFunctionType ft = (IFunctionType) function;
assertTrue(ICPPClassType.class.isInstance((ft.getParameterTypes()[index])));
assertEquals(compositeTypeKey, ((ICPPClassType) ft.getParameterTypes()[index]).getKey());
assertEquals(qn, ASTTypeUtil.getQualifiedName((ICPPClassType) ft.getParameterTypes()[index]));
}
protected static <T> T assertInstance(Object o, Class<T> clazz, Class ... cs) {
assertNotNull("Expected " + clazz.getName() + " but got null", o);
assertTrue("Expected " + clazz.getName() + " but got " + o.getClass().getName(), clazz.isInstance(o));
for (Class c : cs) {
assertTrue("Expected " + clazz.getName() + " but got " + o.getClass().getName(), c.isInstance(o));
}
return clazz.cast(o);
}
protected String readTaggedComment(final String tag) throws IOException {
return TestSourceReader.readTaggedComment(CTestPlugin.getDefault().getBundle(), "parser", getClass(), tag);
}
protected IIndex getIndex() {
return strategy.getIndex();
}
protected static void assertVariable(IBinding b, String qn, Class expType, String expTypeQN) {
assertInstance(b, IVariable.class);
IVariable variable = (IVariable) b;
assertQNEquals(qn, variable);
assertInstance(variable.getType(), expType);
if (expTypeQN != null) {
IType type= variable.getType();
assertInstance(type, IBinding.class);
assertQNEquals(expTypeQN, (IBinding) type);
}
}
protected static void assertTypeContainer(IType conType, String expQN, Class containerType, Class expContainedType, String expContainedTypeQN) {
assertInstance(conType, ITypeContainer.class);
assertInstance(conType, containerType);
IType containedType= ((ITypeContainer) conType).getType();
assertInstance(containedType, expContainedType);
if (expContainedTypeQN != null) {
assertInstance(containedType, IBinding.class);
assertQNEquals(expContainedTypeQN, (IBinding) containedType);
}
}
protected final void checkBindings() {
for (int i = 0; i < strategy.getAstCount(); i++) {
checkBindings(strategy.getAst(i));
}
}
protected final void checkBindings(IASTTranslationUnit ast) {
NameCollector col = new NameCollector();
ast.accept(col);
for (IASTName n : col.nameList) {
if (n.resolveBinding() instanceof IProblemBinding)
fail("ProblemBinding for " + formatForPrinting(n));
}
}
private int getIdentifierOffset(String str) {
for (int i = 0; i < str.length(); ++i) {
if (Character.isJavaIdentifierPart(str.charAt(i)))
return i;
}
fail("Didn't find identifier in \"" + str + "\"");
return -1;
}
private int getIdentifierLength(String str, int offset) {
int i;
for (i = offset; i < str.length() && Character.isJavaIdentifierPart(str.charAt(i)); ++i) {
}
return i - offset;
}
// Get a BindingAssertionHelper for the last AST.
protected BindingAssertionHelper getAssertionHelper() {
int index = strategy.getAstCount() - 1;
return new BindingAssertionHelper(strategy.getAstSource(index).toString(), strategy.getAst(index));
}
static protected class NameCollector extends ASTVisitor {
{
shouldVisitNames = true;
shouldVisitImplicitNames = true;
}
public List<IASTName> nameList = new ArrayList<>();
@Override
public int visit(IASTName name) {
nameList.add(name);
return PROCESS_CONTINUE;
}
public IASTName getName(int idx) {
if (idx < 0 || idx >= nameList.size())
return null;
return nameList.get(idx);
}
public int size() {
return nameList.size();
}
}
protected interface ITestStrategy {
IIndex getIndex();
void setUp() throws Exception;
void tearDown() throws Exception;
public int getAstCount();
public IASTTranslationUnit getAst(int index);
public StringBuilder getAstSource(int index);
public StringBuilder[] getTestData();
public ICProject getCProject();
public boolean isCompositeIndex();
}
private abstract class BaseTestStrategy implements ITestStrategy {
// This method allows tests to specify test-specific flags by including special strings
// in the test source (presumably in a comment).
// Note that it would be insufficient for the tests to do the corresponding actions
// in the test body, because they may need to be done before the indexer runs.
protected void setTestSpecificFlags(String sourceContents) {
// Allow tests to specify that the code contained in the test is allowed to produce
// recursion resolving bidings.
if (sourceContents.contains("special:allowRecursionBindings")) {
System.out.println("setting sAllowRecursionBindings = true");
CPPASTNameBase.sAllowRecursionBindings = true;
}
}
}
class SinglePDOMTestFirstASTStrategy extends BaseTestStrategy {
private IIndex index;
private ICProject cproject;
private StringBuilder[] testData;
private IASTTranslationUnit ast;
private final boolean cpp;
public SinglePDOMTestFirstASTStrategy(boolean cpp) {
this.cpp = cpp;
}
@Override
public ICProject getCProject() {
return cproject;
}
@Override
public StringBuilder[] getTestData() {
return testData;
}
@Override
public int getAstCount() {
return 1;
}
@Override
public IASTTranslationUnit getAst(int index) {
if (index != 0)
throw new IllegalArgumentException();
return ast;
}
@Override
public StringBuilder getAstSource(int index) {
if (index != 0)
throw new IllegalArgumentException();
return testData[1];
}
@Override
public void setUp() throws Exception {
cproject = cpp ?
CProjectHelper.createCCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER) :
CProjectHelper.createCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER);
Bundle b = CTestPlugin.getDefault().getBundle();
testData = TestSourceReader.getContentsForTest(b, "parser", IndexBindingResolutionTestBase.this.getClass(), getName(), 2);
if (testData.length < 2)
fail("Insufficient test data");
testData[1].insert(0, "#include \"header.h\" " + END_OF_ADDED_CODE_MARKER + "\n");
String headerContents = testData[0].toString();
String sourceContents = testData[1].toString();
setTestSpecificFlags(sourceContents);
IFile file = TestSourceReader.createFile(cproject.getProject(), new Path("header.h"), headerContents);
CCorePlugin.getIndexManager().setIndexerId(cproject, IPDOMManager.ID_FAST_INDEXER);
waitForIndexer(cproject);
if (DEBUG) {
System.out.println("Project PDOM: " + getName());
((PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject)).accept(new PDOMPrettyPrinter());
}
index= CCorePlugin.getIndexManager().getIndex(cproject);
index.acquireReadLock();
IFile cppfile= TestSourceReader.createFile(cproject.getProject(), new Path("references.c" + (cpp ? "pp" : "")), sourceContents);
ast = TestSourceReader.createIndexBasedAST(index, cproject, cppfile);
}
@Override
public void tearDown() throws Exception {
if (index != null) {
index.releaseReadLock();
}
if (cproject != null) {
cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, new NullProgressMonitor());
}
}
@Override
public IIndex getIndex() {
return index;
}
@Override
public boolean isCompositeIndex() {
return false;
}
}
protected class SinglePDOMTestStrategy extends BaseTestStrategy {
private IIndex index;
private ICProject cproject;
private StringBuilder[] testData;
private IASTTranslationUnit ast;
private final boolean cpp;
private final boolean shouldRequireHeaderFile;
public SinglePDOMTestStrategy(boolean cpp) {
this(cpp, true);
}
public SinglePDOMTestStrategy(boolean cpp, boolean shouldRequireHeaderFile) {
this.cpp = cpp;
this.shouldRequireHeaderFile = shouldRequireHeaderFile;
}
@Override
public ICProject getCProject() {
return cproject;
}
@Override
public StringBuilder[] getTestData() {
return testData;
}
@Override
public int getAstCount() {
return 1;
}
@Override
public IASTTranslationUnit getAst(int index) {
if (index != 0)
throw new IllegalArgumentException();
return ast;
}
@Override
public StringBuilder getAstSource(int index) {
if (index != 0)
throw new IllegalArgumentException();
return testData[1];
}
@Override
public void setUp() throws Exception {
cproject = cpp ?
CProjectHelper.createCCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER) :
CProjectHelper.createCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER);
Bundle b = CTestPlugin.getDefault().getBundle();
testData = TestSourceReader.getContentsForTest(b, "parser", IndexBindingResolutionTestBase.this.getClass(), getName(), 2);
if(testData.length < 1) {
fail("Insufficient test data");
} else if(shouldRequireHeaderFile && testData.length == 1) {
fail("Insufficient test data");
} else if(testData.length == 1) {
StringBuilder newTestData[] = new StringBuilder[2];
newTestData[0] = new StringBuilder();
newTestData[1] = testData[0];
testData = newTestData;
}
testData[1].insert(0, "#include \"header.h\" " + END_OF_ADDED_CODE_MARKER + "\n");
String headerContents = testData[0].toString();
String sourceContents = testData[1].toString();
setTestSpecificFlags(sourceContents);
IFile file = TestSourceReader.createFile(cproject.getProject(), new Path("header.h"), headerContents);
CCorePlugin.getIndexManager().setIndexerId(cproject, IPDOMManager.ID_FAST_INDEXER);
waitForIndexer(cproject);
IFile cppfile= TestSourceReader.createFile(cproject.getProject(), new Path("references.c" + (cpp ? "pp" : "")), sourceContents);
waitForIndexer(cproject);
if (DEBUG) {
System.out.println("Project PDOM: " + getName());
((PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject)).accept(new PDOMPrettyPrinter());
}
index= CCorePlugin.getIndexManager().getIndex(cproject);
index.acquireReadLock();
ast = TestSourceReader.createIndexBasedAST(index, cproject, cppfile);
}
@Override
public void tearDown() throws Exception {
if (index != null) {
index.releaseReadLock();
}
if (cproject != null) {
cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, new NullProgressMonitor());
}
}
@Override
public IIndex getIndex() {
return index;
}
@Override
public boolean isCompositeIndex() {
return false;
}
}
/**
* This strategy allows tests to create an arbitrary number of header and source files
* and to obtain ASTs of any subset of the created files.
*
* The first line of each comment section preceding the test contains the name of the file
* to put the contents of the section to. To request the AST of a file, put an asterisk after
* the file name.
*
* If the same file name is repeated more than once, the file will be created and then updated
* with the new contents. It is guaranteed that the indexer will run for the original and then
* for the updated file contents.
*/
protected class SinglePDOMTestNamedFilesStrategy implements ITestStrategy {
private IIndex index;
private ICProject cproject;
private StringBuilder[] testData;
private final List<StringBuilder> astSources;
private final List<IASTTranslationUnit> asts;
private final boolean cpp;
public SinglePDOMTestNamedFilesStrategy(boolean cpp) {
this.cpp = cpp;
astSources = new ArrayList<>();
asts = new ArrayList<>();
}
@Override
public ICProject getCProject() {
return cproject;
}
@Override
public StringBuilder[] getTestData() {
return testData;
}
@Override
public int getAstCount() {
return asts.size();
}
@Override
public IASTTranslationUnit getAst(int index) {
return asts.get(index);
}
@Override
public StringBuilder getAstSource(int index) {
return astSources.get(index);
}
@Override
public void setUp() throws Exception {
cproject = cpp ?
CProjectHelper.createCCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER) :
CProjectHelper.createCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER);
Bundle b = CTestPlugin.getDefault().getBundle();
testData = TestSourceReader.getContentsForTest(b, "parser", IndexBindingResolutionTestBase.this.getClass(), getName(), 0);
List<IFile> astFiles = new ArrayList<>();
for (int i = 0; i < testData.length;) {
Set<String> createdFiles = new HashSet<>();
for (int j = i; j < testData.length; j++, i++) {
StringBuilder contents = testData[j];
int endOfLine = contents.indexOf("\n");
if (endOfLine >= 0) {
endOfLine++;
} else {
endOfLine = contents.length();
}
String filename = contents.substring(0, endOfLine).trim();
boolean astRequested = filename.endsWith("*");
if (astRequested) {
filename = filename.substring(0, filename.length() - 1).trim();
}
if (!createdFiles.add(filename)) {
// The file has already been encountered since the project was indexed.
// Wait for the indexer before updating the file.
break;
}
contents.delete(0, endOfLine); // Remove first line from the file contents.
IFile file = TestSourceReader.createFile(cproject.getProject(), new Path(filename), contents.toString());
if (astRequested || (j == testData.length - 1 && astFiles.isEmpty())) {
int pos = astFiles.indexOf(file);
if (pos < 0) {
astFiles.add(file);
astSources.add(contents);
} else {
astSources.set(pos, contents);
}
}
}
CCorePlugin.getIndexManager().setIndexerId(cproject, IPDOMManager.ID_FAST_INDEXER);
waitForIndexer(cproject);
}
if (DEBUG) {
System.out.println("Project PDOM: " + getName());
((PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject)).accept(new PDOMPrettyPrinter());
}
index= CCorePlugin.getIndexManager().getIndex(cproject);
index.acquireReadLock();
for (IFile file : astFiles) {
asts.add(TestSourceReader.createIndexBasedAST(index, cproject, file));
}
}
@Override
public void tearDown() throws Exception {
if (index != null) {
index.releaseReadLock();
}
if (cproject != null) {
cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, new NullProgressMonitor());
}
}
@Override
public IIndex getIndex() {
return index;
}
@Override
public boolean isCompositeIndex() {
return false;
}
}
class ReferencedProject extends BaseTestStrategy {
private IIndex index;
private ICProject cproject, referenced;
private StringBuilder[] testData;
private IASTTranslationUnit ast;
private final boolean cpp;
public ReferencedProject(boolean cpp) {
this.cpp = cpp;
}
@Override
public ICProject getCProject() {
return cproject;
}
@Override
public void tearDown() throws Exception {
if (index != null) {
index.releaseReadLock();
}
if (cproject != null) {
cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, new NullProgressMonitor());
}
if (referenced != null) {
referenced.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, new NullProgressMonitor());
}
}
@Override
public void setUp() throws Exception {
cproject= cpp ?
CProjectHelper.createCCProject("OnlineContent"+System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER) :
CProjectHelper.createCProject("OnlineContent"+System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER);
Bundle b= CTestPlugin.getDefault().getBundle();
testData= TestSourceReader.getContentsForTest(b, "parser", IndexBindingResolutionTestBase.this.getClass(), getName(), 2);
if (testData.length < 2)
fail("Insufficient test data");
testData[1].insert(0, "#include \"header.h\" " + END_OF_ADDED_CODE_MARKER + "\n");
String sourceContents = testData[1].toString();
setTestSpecificFlags(sourceContents);
referenced = createReferencedContent();
TestScannerProvider.sIncludes= new String[] {referenced.getProject().getLocation().toOSString()};
IFile references= TestSourceReader.createFile(cproject.getProject(), new Path("refs.c" + (cpp ? "pp" : "")), sourceContents);
IProject[] refs = new IProject[] {referenced.getProject()};
IProjectDescription pd = cproject.getProject().getDescription();
pd.setReferencedProjects(refs);
cproject.getProject().setDescription(pd, new NullProgressMonitor());
IndexerPreferences.set(cproject.getProject(), IndexerPreferences.KEY_INDEXER_ID, IPDOMManager.ID_FAST_INDEXER);
CCorePlugin.getIndexManager().reindex(cproject);
waitForIndexer(cproject);
if (DEBUG) {
System.out.println("Online: " + getName());
((PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject)).accept(new PDOMPrettyPrinter());
}
index= CCorePlugin.getIndexManager().getIndex(cproject, IIndexManager.ADD_DEPENDENCIES);
index.acquireReadLock();
ast= TestSourceReader.createIndexBasedAST(index, cproject, references);
}
private ICProject createReferencedContent() throws Exception {
ICProject referenced = cpp ?
CProjectHelper.createCCProject("ReferencedContent" + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER) :
CProjectHelper.createCProject("ReferencedContent" + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER);
String content = testData[0].toString();
IFile file = TestSourceReader.createFile(referenced.getProject(), new Path("header.h"), content);
IndexerPreferences.set(referenced.getProject(), IndexerPreferences.KEY_INDEXER_ID, IPDOMManager.ID_FAST_INDEXER);
CCorePlugin.getIndexManager().reindex(referenced);
waitForIndexer(referenced);
if (DEBUG) {
System.out.println("Referenced: " + getName());
((PDOM) CCoreInternals.getPDOMManager().getPDOM(referenced)).accept(new PDOMPrettyPrinter());
}
return referenced;
}
@Override
public int getAstCount() {
return 1;
}
@Override
public IASTTranslationUnit getAst(int index) {
if (index != 0)
throw new IllegalArgumentException();
return ast;
}
@Override
public StringBuilder getAstSource(int index) {
if (index != 0)
throw new IllegalArgumentException();
return testData[1];
}
@Override
public IIndex getIndex() {
return index;
}
@Override
public StringBuilder[] getTestData() {
return testData;
}
@Override
public boolean isCompositeIndex() {
return true;
}
}
/**
* When a test is failing only for the strategy where the test data is split over
* multiple index fragments, we artificially fail the single fragment strategy also.
* This is not ideal, but as both strategies behavior are typically the same, is
* quite rare.
*/
protected void fakeFailForSingle() {
if (getName().startsWith("_") && strategy instanceof SinglePDOMTestStrategy) {
fail("Artificially failing - see IndexBindingResolutionTestBase.fakeFailForSingle()");
}
}
/**
* When a test is failing only for the strategy where the test data is not split over
* multiple index fragments, we artificially fail the single fragment strategy also.
* This is not ideal, but as both strategies behavior are typically the same, is
* quite rare.
*/
protected void fakeFailForMultiProject() {
if (getName().startsWith("_") && strategy instanceof ReferencedProject) {
fail("Artificially failing - see IndexBindingResolutionTestBase.fakeFailForReferenced()");
}
}
protected static void assertSameType(IType first, IType second){
assertNotNull(first);
assertNotNull(second);
assertTrue("Expected types to be the same, but first was: '" + first.toString() + "' and second was: '" + second + "'", first.isSameType(second));
}
}