/*******************************************************************************
 * Copyright (c) 2007, 2017 Wind River Systems, Inc. and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Markus Schorn - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.internal.index.tests;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

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.DOMException;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IEnumerator;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IParameter;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPEnumeration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPField;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMember;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespaceAlias;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateNonTypeParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateTypeParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IndexFilter;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.testplugin.CProjectHelper;
import org.eclipse.cdt.core.testplugin.util.TestSourceReader;
import org.eclipse.cdt.internal.core.pdom.CModelListener;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMFile;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Path;

import junit.framework.TestSuite;

public class IndexUpdateTests extends IndexTestBase {
	private static final String EXPLICIT = "explicit";
	private static final String VIRTUAL = "virtual";
	private static final String PURE_VIRTUAL = "pure-virtual";
	private static final String PROTECTED = "protected";
	private static final String PUBLIC = "public";
	private static final String PRIVATE = "private";
	private static final String CHAR = "char";
	private static final String INLINE = "inline";
	private static final String MUTABLE = "mutable";
	private static final String STATIC = "static";
	private static final String REGISTER = "register";
	private static final String AUTO = "auto";
	private static final String SHORT = "short int";
	private static final String INT = "int";
	private static final String IMPLICIT = "implicit";

	public static TestSuite suite() {
		TestSuite suite = suite(IndexUpdateTests.class, "_");
		suite.addTest(new IndexUpdateTests("deleteProject"));
		return suite;
	}

	private ICProject fCppProject;
	private ICProject fCProject;
	private IIndex fIndex;
	private CharSequence[] fContents;
	private IFile fFile;
	private IFile fHeader;
	private int fContentUsed;

	public IndexUpdateTests(String name) {
		super(name);
	}

	@Override
	public void setUp() throws Exception {
		super.setUp();
		if (fCppProject == null) {
			fCppProject = CProjectHelper.createCCProject("indexUpdateTestsCpp", null, IPDOMManager.ID_FAST_INDEXER);
		}
		if (fCProject == null) {
			fCProject = CProjectHelper.createCProject("indexUpdateTestsC", null, IPDOMManager.ID_FAST_INDEXER);
		}
		waitForIndexer(fCppProject);
		waitForIndexer(fCProject);
		fIndex = CCorePlugin.getIndexManager().getIndex(new ICProject[] { fCProject, fCppProject });
	}

	private void setupHeader(int totalFileVersions, boolean cpp) throws Exception {
		if (fContents == null) {
			fContents = getContentsForTest(totalFileVersions);
			fContentUsed = -1;
		}
		IProject project = cpp ? fCppProject.getProject() : fCProject.getProject();
		fHeader = TestSourceReader.createFile(project, "header.h", fContents[++fContentUsed].toString());
		waitForIndexer(fCppProject);
		waitForIndexer(fCProject);
	}

	private void updateHeader() throws Exception {
		// Append variable comment to the end of the file to change its contents.
		// Indexer would not reindex the file if its contents remain the same.
		IProject project = fHeader.getProject();
		fHeader = TestSourceReader.createFile(project, "header.h",
				fContents[++fContentUsed].toString() + "\n// " + fContentUsed);
		waitUntilFileIsIndexed(fIndex, fHeader);
	}

	private void setupFile(int totalFileVersions, boolean cpp) throws Exception {
		if (fContents == null) {
			fContents = getContentsForTest(totalFileVersions);
			fContentUsed = -1;
		}
		ICProject cproject = cpp ? fCppProject : fCProject;
		fFile = TestSourceReader.createFile(cproject.getProject(), "file" + (cpp ? ".cpp" : ".c"),
				fContents[++fContentUsed].toString());
		TestSourceReader.waitUntilFileIsIndexed(fIndex, fFile, INDEXER_TIMEOUT_MILLISEC);
		waitForIndexer(cproject);
	}

	private void updateFile() throws Exception {
		// Append variable comment to the end of the file to change its contents.
		// Indexer would not reindex the file if its contents remain the same.
		fFile = TestSourceReader.createFile(fFile.getParent(), fFile.getName(),
				fContents[++fContentUsed].toString() + "\n// " + fContentUsed);
		waitUntilFileIsIndexed(fIndex, fFile);
	}

	@Override
	public void tearDown() throws Exception {
		fIndex = null;
		if (fFile != null) {
			fFile.delete(true, npm());
		}
		if (fHeader != null) {
			fHeader.delete(true, npm());
		}
		super.tearDown();
	}

	public void deleteProject() {
		if (fCProject != null) {
			CProjectHelper.delete(fCProject);
			fCProject = null;
		}
		if (fCppProject != null) {
			CProjectHelper.delete(fCppProject);
			fCppProject = null;
		}
	}

	// int globalVar;

	// short globalVar;

	// auto int globalVar;

	// register int globalVar;
	public void testGlobalCVariable() throws Exception {
		setupFile(4, false);
		checkVariable("globalVar", INT, new String[] {});
		updateFile();
		checkVariable("globalVar", SHORT, new String[] {});
		updateFile();
		checkVariable("globalVar", INT, new String[] { AUTO });
		updateFile();
		checkVariable("globalVar", INT, new String[] { REGISTER });
	}

	private void checkVariable(String name, String type, String[] modifiers) throws Exception {
		fIndex.acquireReadLock();
		try {
			IVariable var = (IVariable) findBinding(name);
			checkVariable(var, type, modifiers);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	private void checkValue(String name, Long value) throws Exception {
		fIndex.acquireReadLock();
		try {
			IBinding b = findBinding(name);
			IValue v = null;
			if (b instanceof IVariable)
				v = ((IVariable) b).getInitialValue();
			else if (b instanceof IEnumerator)
				v = ((IEnumerator) b).getValue();
			else
				fail();

			if (value == null)
				assertNull(v);
			else
				assertEquals(value, v.numberValue());
		} finally {
			fIndex.releaseReadLock();
		}
	}

	private void checkVariable(IVariable var, String type, String[] modifiers) throws DOMException {
		assertEquals(msg(), type, ASTTypeUtil.getType(var.getType()));
		checkModifier(modifiers, AUTO, var.isAuto());
		checkModifier(modifiers, REGISTER, var.isRegister());
		checkModifier(modifiers, STATIC, var.isStatic());
	}

	private void checkModifier(String[] modifiers, String modifier, boolean actual) throws DOMException {
		assertEquals(msg(modifier), hasMod(modifiers, modifier), actual);
	}

	private boolean hasMod(String[] modifiers, String mod) {
		return Arrays.asList(modifiers).contains(mod);
	}

	private IIndexBinding findBinding(String name) throws CoreException {
		String[] names = name.split("::");
		char[][] nchars = new char[names.length][];
		for (int i = 0; i < nchars.length; i++) {
			nchars[i] = names[i].toCharArray();
		}
		IIndexBinding[] bindings = fIndex.findBindings(nchars, IndexFilter.ALL_DECLARED, npm());
		return bindings.length > 0 ? bindings[0] : null;
	}

	private String msg() {
		return "Update #" + fContentUsed;
	}

	private String msg(String modifier) {
		return msg() + "; " + modifier + ":";
	}

	// int globalVar;

	// short globalVar;

	// register int globalVar;
	public void testGlobalCppVariable() throws Exception {
		setupFile(3, true);
		checkCppVariable("globalVar", INT, new String[] {});
		updateFile();
		checkCppVariable("globalVar", SHORT, new String[] {});
		updateFile();
		checkCppVariable("globalVar", INT, new String[] {});
	}

	private void checkCppVariable(String name, String type, String[] modifiers) throws Exception {
		fIndex.acquireReadLock();
		try {
			ICPPVariable var = (ICPPVariable) findBinding(name);
			checkCppVariable(var, type, modifiers);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	private void checkCppVariable(ICPPVariable var, String type, String[] modifiers) throws Exception {
		checkVariable(var, type, modifiers);
		checkModifier(modifiers, MUTABLE, var.isMutable());
	}

	// int globalFunction(int a, int b){};

	// short globalFunction(int a, int b){};

	// int globalFunction(char a){};

	// inline int globalFunction(char a){};
	public void testCFunction() throws Exception {
		setupFile(4, false);
		checkFunction("globalFunction", new String[] { INT, INT, INT }, new String[] {});
		updateFile();
		checkFunction("globalFunction", new String[] { SHORT, INT, INT }, new String[] {});
		updateFile();
		checkFunction("globalFunction", new String[] { INT, CHAR }, new String[] {});
		updateFile();
		checkFunction("globalFunction", new String[] { INT, CHAR }, new String[] { INLINE });
	}

	private void checkFunction(String name, String[] types, String[] modifiers) throws Exception {
		fIndex.acquireReadLock();
		try {
			IFunction func = (IFunction) findBinding(name);
			checkFunction(func, types, modifiers);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	private void checkFunction(IFunction func, String[] types, String[] modifiers) throws DOMException {
		assertEquals(msg(), types[0], ASTTypeUtil.getType(func.getType().getReturnType()));
		IParameter[] params = func.getParameters();
		assertEquals(msg(), types.length - 1, params.length);
		for (int i = 0; i < params.length; i++) {
			IParameter parameter = params[i];
			assertEquals(msg(), types[i + 1], ASTTypeUtil.getType(parameter.getType()));
		}
		checkModifier(modifiers, INLINE, func.isInline());
		checkModifier(modifiers, STATIC, func.isStatic());
	}

	// int globalFunction(int a, int b){};

	// short globalFunction(int a, int b){};

	// int globalFunction(char a){};

	// inline int globalFunction(char a){};
	public void testCppFunction() throws Exception {
		setupFile(4, true);
		checkFunction("globalFunction", new String[] { INT, INT, INT }, new String[] {});
		updateFile();
		checkFunction("globalFunction", new String[] { SHORT, INT, INT }, new String[] {});
		updateFile();
		checkFunction("globalFunction", new String[] { INT, CHAR }, new String[] {});
		updateFile();
		checkFunction("globalFunction", new String[] { INT, CHAR }, new String[] { INLINE });
	}

	// struct my_struct {int fField;};

	// struct my_struct {short fField;};
	public void testCField() throws Exception {
		setupFile(2, false);
		checkVariable("my_struct::fField", INT, new String[] {});
		updateFile();
		checkVariable("my_struct::fField", SHORT, new String[] {});
	}

	// class MyClass {int fField;};

	// class MyClass {short fField;};

	// class MyClass {mutable int fField;};

	// class MyClass {public: int fField;};

	// class MyClass {protected: int fField;};

	// class MyClass {private: int fField;};

	// class MyClass {private: static int fField;};
	public void testCppField() throws Exception {
		setupFile(7, true);
		checkCppField("MyClass::fField", INT, new String[] { PRIVATE });
		updateFile();
		checkCppField("MyClass::fField", SHORT, new String[] { PRIVATE });
		updateFile();
		checkCppField("MyClass::fField", INT, new String[] { PRIVATE, MUTABLE });
		updateFile();
		checkCppField("MyClass::fField", INT, new String[] { PUBLIC });
		updateFile();
		checkCppField("MyClass::fField", INT, new String[] { PROTECTED });
		updateFile();
		checkCppField("MyClass::fField", INT, new String[] { PRIVATE });
		updateFile();
		checkCppField("MyClass::fField", INT, new String[] { PRIVATE, STATIC });
	}

	private void checkCppField(String name, String type, String[] modifiers) throws Exception {
		fIndex.acquireReadLock();
		try {
			ICPPField field = (ICPPField) findBinding(name);
			checkCppVariable(field, type, modifiers);
			checkCppMember(field, modifiers);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	private void checkCppMember(ICPPMember member, String[] modifiers) throws Exception {
		int visibility = member.getVisibility();
		checkModifier(modifiers, PUBLIC, visibility == ICPPMember.v_public);
		checkModifier(modifiers, PROTECTED, visibility == ICPPMember.v_protected);
		checkModifier(modifiers, PRIVATE, visibility == ICPPMember.v_private);
	}

	// class MyClass {int method(int a, int b);};

	// class MyClass {short method(int a, int b);};

	// class MyClass {int method(char a);};

	// class MyClass {inline int method(char a);};

	// class MyClass {virtual int method(char a);};

	// class MyClass {public: int method(char a);};

	// class MyClass {protected: int method(char a);};

	// class MyClass {private: int method(char a);};

	// class MyClass {int method(char a){};};

	// class MyClass {virtual int method(char a) = 0;};
	public void testCppMethod() throws Exception {
		setupFile(10, true);
		checkCppMethod("MyClass::method", new String[] { INT, INT, INT }, new String[] { PRIVATE });
		updateFile();
		checkCppMethod("MyClass::method", new String[] { SHORT, INT, INT }, new String[] { PRIVATE });
		updateFile();
		checkCppMethod("MyClass::method", new String[] { INT, CHAR }, new String[] { PRIVATE });
		updateFile();
		checkCppMethod("MyClass::method", new String[] { INT, CHAR }, new String[] { PRIVATE, INLINE });
		updateFile();
		checkCppMethod("MyClass::method", new String[] { INT, CHAR }, new String[] { PRIVATE, VIRTUAL });
		updateFile();
		checkCppMethod("MyClass::method", new String[] { INT, CHAR }, new String[] { PUBLIC });
		updateFile();
		checkCppMethod("MyClass::method", new String[] { INT, CHAR }, new String[] { PROTECTED });
		updateFile();
		checkCppMethod("MyClass::method", new String[] { INT, CHAR }, new String[] { PRIVATE });
		updateFile();
		checkCppMethod("MyClass::method", new String[] { INT, CHAR }, new String[] { PRIVATE, INLINE });
		updateFile();
		checkCppMethod("MyClass::method", new String[] { INT, CHAR }, new String[] { PRIVATE, VIRTUAL, PURE_VIRTUAL });
	}

	// class MyClass {protected: int method(int a, int b);};

	// #include "header.h"
	// int MyClass::method(int a, int b);

	// #include "header.h"
	// char MyClass::method(int a, int b);
	public void testFixedCppMethod() throws Exception {
		setupHeader(3, true);
		checkCppMethod("MyClass::method", new String[] { INT, INT, INT }, new String[] { PROTECTED });
		setupFile(0, true);
		checkCppMethod("MyClass::method", new String[] { INT, INT, INT }, new String[] { PROTECTED });
		updateFile();
		checkCppMethod("MyClass::method", new String[] { INT, INT, INT }, new String[] { PROTECTED });
	}

	private void checkCppMethod(String name, String[] types, String[] modifiers) throws Exception {
		fIndex.acquireReadLock();
		try {
			ICPPMethod method = (ICPPMethod) findBinding(name);
			checkCppMethod(method, types, modifiers);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	private void checkCppMethod(ICPPMethod method, String[] types, String[] modifiers) throws DOMException, Exception {
		checkFunction(method, types, modifiers);
		checkCppMember(method, modifiers);
		checkModifier(modifiers, VIRTUAL, method.isVirtual());
		checkModifier(modifiers, PURE_VIRTUAL, method.isPureVirtual());
		checkModifier(modifiers, IMPLICIT, method.isImplicit());
	}

	// class MyClass {MyClass(int a, int b);};

	// class MyClass {MyClass(char a, int b);};

	// class MyClass {explicit MyClass(char a, int b);};

	// class MyClass {public: MyClass(char a, int b);};

	// class MyClass {protected: MyClass(char a, int b);};

	// class MyClass {private: MyClass(char a, int b);};
	public void testCppConstructor() throws Exception {
		setupFile(6, true);
		checkCppConstructor("MyClass::MyClass", new String[] { "", INT, INT }, new String[] { PRIVATE });
		updateFile();
		checkCppConstructor("MyClass::MyClass", new String[] { "", CHAR, INT }, new String[] { PRIVATE });
		updateFile();
		checkCppConstructor("MyClass::MyClass", new String[] { "", CHAR, INT }, new String[] { PRIVATE, EXPLICIT });
		updateFile();
		checkCppConstructor("MyClass::MyClass", new String[] { "", CHAR, INT }, new String[] { PUBLIC });
		updateFile();
		checkCppConstructor("MyClass::MyClass", new String[] { "", CHAR, INT }, new String[] { PROTECTED });
		updateFile();
		checkCppConstructor("MyClass::MyClass", new String[] { "", CHAR, INT }, new String[] { PRIVATE });
	}

	private void checkCppConstructor(String name, String[] types, String[] modifiers) throws Exception {
		fIndex.acquireReadLock();
		try {
			ICPPConstructor ctor = (ICPPConstructor) findBinding(name);
			checkCppConstructor(ctor, types, modifiers);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	private void checkCppConstructor(ICPPConstructor ctor, String[] types, String[] modifiers) throws Exception {
		checkFunction(ctor, types, modifiers);
		checkCppMember(ctor, modifiers);
		checkModifier(modifiers, EXPLICIT, ctor.isExplicit());
	}

	// class MyClass {};

	// class MyClass {protected: MyClass(void);};

	// class MyClass {explicit MyClass(const MyClass& rhs);};

	// class MyClass {public: MyClass& operator=(const MyClass& rhs) {}};

	// class MyClass {};
	public void testImplicitMethods() throws Exception {
		setupFile(5, true);
		checkImplicitMethods("MyClass", new String[] { IMPLICIT, PUBLIC }, new String[] { IMPLICIT, PUBLIC },
				new String[] { IMPLICIT, PUBLIC });
		updateFile();
		checkImplicitMethods("MyClass", new String[] { PROTECTED }, new String[] { IMPLICIT, PUBLIC },
				new String[] { IMPLICIT, PUBLIC });
		updateFile();
		checkImplicitMethods("MyClass", null, // no default constructor, because we declared the copy constructor.
				new String[] { EXPLICIT, PRIVATE }, new String[] { IMPLICIT, PUBLIC });
		updateFile();
		checkImplicitMethods("MyClass", new String[] { IMPLICIT, PUBLIC }, new String[] { IMPLICIT, PUBLIC },
				new String[] { INLINE, PUBLIC });
		updateFile();
		checkImplicitMethods("MyClass", new String[] { IMPLICIT, PUBLIC }, new String[] { IMPLICIT, PUBLIC },
				new String[] { IMPLICIT, PUBLIC });
	}

	private void checkImplicitMethods(String name, String[] m1, String[] m2, String[] m3) throws Exception {
		fIndex.acquireReadLock();
		try {
			final char[] nchars = name.toCharArray();
			final String refType = name + " &";
			final String constRefType = "const " + refType;
			IIndexBinding[] ctors = fIndex.findBindings(new char[][] { nchars, nchars },
					IndexFilter.ALL_DECLARED_OR_IMPLICIT, npm());

			int count = 0;
			for (int i = 0; i < ctors.length; i++) {
				IIndexBinding ctor = ctors[i];
				if (ctor.isFileLocal()) {
					ctors[count++] = ctor;
				}
			}
			assertEquals(m1 == null ? 1 : 2, count);
			final IType[] parameterTypes = ((ICPPConstructor) ctors[0]).getType().getParameterTypes();
			if (parameterTypes.length != 1 || !(parameterTypes[0] instanceof ICPPReferenceType)) {
				IIndexBinding h = ctors[0];
				ctors[0] = ctors[1];
				ctors[1] = h;
			}
			if (m1 != null) {
				checkCppConstructor((ICPPConstructor) ctors[1], new String[] { "" }, m1);
			}
			checkCppConstructor((ICPPConstructor) ctors[0], new String[] { "", constRefType }, m2);

			IIndexBinding[] assignmentOps = fIndex.findBindings(new char[][] { nchars, "operator =".toCharArray() },
					IndexFilter.ALL_DECLARED_OR_IMPLICIT, npm());
			count = 0;
			for (int i = 0; i < assignmentOps.length; i++) {
				IIndexBinding assignmentOp = assignmentOps[i];
				if (assignmentOp.isFileLocal()) {
					assignmentOps[count++] = assignmentOp;
				}
			}
			assertEquals(1, count);
			checkCppMethod((ICPPMethod) assignmentOps[0], new String[] { refType, constRefType }, m3);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// typedef int myType;

	// typedef short myType;
	public void testCTypedef() throws Exception {
		setupFile(2, false);
		checkTypedef("myType", INT);
		updateFile();
		checkTypedef("myType", SHORT);
	}

	private void checkTypedef(String name, String type) throws Exception {
		fIndex.acquireReadLock();
		try {
			ITypedef var = (ITypedef) findBinding(name);
			checkTypedef(var, type);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	private void checkTypedef(ITypedef var, String type) throws DOMException {
		assertEquals(msg(), type, ASTTypeUtil.getType(var.getType()));
	}

	// typedef int myType;

	// typedef short myType;
	public void testCppTypedef() throws Exception {
		setupFile(2, true);
		checkTypedef("myType", INT);
		updateFile();
		checkTypedef("myType", SHORT);
	}

	// namespace aNs {
	// }
	// namespace nsAlias= aNs;

	// namespace bNs {
	// }
	// namespace nsAlias= bNs;
	public void testNamespaceAlias() throws Exception {
		setupFile(2, true);
		checkNamespaceAlias("nsAlias", "aNs");
		updateFile();
		checkNamespaceAlias("nsAlias", "bNs");
	}

	private void checkNamespaceAlias(String name, String target) throws Exception {
		fIndex.acquireReadLock();
		try {
			ICPPNamespaceAlias nsalias = (ICPPNamespaceAlias) findBinding(name);
			assertEquals(msg(), target, nsalias.getBinding().getName());
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// struct myType {
	//    int a;
	// };

	// union myType {
	//    int a;
	// };

	// typedef int myType;

	// enum myType {};
	public void testChangingTypePlainC() throws Exception {
		setupFile(4, false);
		IBinding binding;
		ICompositeType ct;
		fIndex.acquireReadLock();
		try {
			binding = findBinding("myType");
			assertTrue(binding instanceof ICompositeType);
			ct = (ICompositeType) binding;
			assertTrue(ct.getKey() == ICompositeType.k_struct);
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			binding = findBinding("myType");
			assertTrue(binding instanceof ICompositeType);
			ct = (ICompositeType) binding;
			assertTrue(ct.getKey() == ICompositeType.k_union);
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			binding = findBinding("myType");
			assertTrue(binding instanceof ITypedef);
			ITypedef td = (ITypedef) binding;
			assertEquals(INT, ASTTypeUtil.getType(td.getType()));
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			binding = findBinding("myType");
			assertTrue(binding instanceof IEnumeration);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// class myType {
	//    int a;
	// };

	// struct myType {
	//    int a;
	// };

	// union myType {
	//    int a;
	// };

	// typedef int myType;

	// enum myType {};
	public void testChangingTypeCPP() throws Exception {
		setupFile(4, true);
		IBinding binding;
		ICompositeType ct;
		fIndex.acquireReadLock();
		try {
			binding = findBinding("myType");
			assertTrue(binding instanceof ICompositeType);
			ct = (ICompositeType) binding;
			assertTrue(ct.getKey() == ICompositeType.k_struct);
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			binding = findBinding("myType");
			assertTrue(binding instanceof ICompositeType);
			ct = (ICompositeType) binding;
			assertTrue(ct.getKey() == ICompositeType.k_union);
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			binding = findBinding("myType");
			assertTrue(binding instanceof ITypedef);
			ITypedef td = (ITypedef) binding;
			assertEquals(INT, ASTTypeUtil.getType(td.getType()));
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			binding = findBinding("myType");
			assertTrue(binding instanceof IEnumeration);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// template<typename T> class CT {};

	// template<typename T=int> class CT {};

	// template<typename T=char> class CT {};

	// template<int U, typename T> struct CT {};

	// template<template<typename T> class V> class CT {};

	// template<template<template<typename I> class T> class V> class CT {};

	// template<typename U> class CT {};
	public void testClassTemplates() throws Exception {
		setupFile(7, true);
		ICPPClassTemplate binding;
		ICompositeType ct;
		fIndex.acquireReadLock();
		long pdomid;
		try {
			binding = (ICPPClassTemplate) findBinding("CT");
			assertEquals(ICPPClassType.k_class, binding.getKey());
			ICPPTemplateParameter[] tpars = binding.getTemplateParameters();
			assertEquals(1, tpars.length);
			assertTrue(tpars[0] instanceof ICPPTemplateTypeParameter);
			assertEquals(0, tpars[0].getParameterID());
			assertEquals("T", tpars[0].getName());
			assertNull(tpars[0].getDefaultValue());
			pdomid = ((IAdaptable) tpars[0]).getAdapter(PDOMNode.class).getRecord();
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			binding = (ICPPClassTemplate) findBinding("CT");
			assertEquals(ICPPClassType.k_class, binding.getKey());
			ICPPTemplateParameter[] tpars = binding.getTemplateParameters();
			assertEquals(1, tpars.length);
			assertTrue(tpars[0] instanceof ICPPTemplateTypeParameter);
			assertEquals(0, tpars[0].getParameterID());
			assertEquals("T", tpars[0].getName());
			assertEquals("int", ASTTypeUtil.getType(tpars[0].getDefaultValue().getTypeValue()));
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			binding = (ICPPClassTemplate) findBinding("CT");
			assertEquals(ICPPClassType.k_class, binding.getKey());
			ICPPTemplateParameter[] tpars = binding.getTemplateParameters();
			assertEquals(1, tpars.length);
			assertTrue(tpars[0] instanceof ICPPTemplateTypeParameter);
			assertEquals(0, tpars[0].getParameterID());
			assertEquals("T", tpars[0].getName());
			assertEquals("char", ASTTypeUtil.getType(tpars[0].getDefaultValue().getTypeValue()));
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			binding = (ICPPClassTemplate) findBinding("CT");
			assertEquals(ICompositeType.k_struct, binding.getKey());
			ICPPTemplateParameter[] tpars = binding.getTemplateParameters();
			assertEquals(2, tpars.length);
			assertTrue(tpars[0] instanceof ICPPTemplateNonTypeParameter);
			assertEquals("U", tpars[0].getName());
			assertEquals(0, tpars[0].getParameterID());
			assertTrue(tpars[1] instanceof ICPPTemplateTypeParameter);
			assertEquals("T", tpars[1].getName());
			assertEquals(1, tpars[1].getParameterID());
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			binding = (ICPPClassTemplate) findBinding("CT");
			assertEquals(ICPPClassType.k_class, binding.getKey());
			ICPPTemplateParameter[] tpars = binding.getTemplateParameters();
			assertEquals(1, tpars.length);
			assertTrue(tpars[0] instanceof ICPPTemplateTemplateParameter);
			assertEquals("V", tpars[0].getName());
			assertEquals(0, tpars[0].getParameterID());
			tpars = ((ICPPTemplateTemplateParameter) tpars[0]).getTemplateParameters();
			assertEquals(1, tpars.length);
			assertTrue(tpars[0] instanceof ICPPTemplateTypeParameter);
			assertEquals(0x10000, tpars[0].getParameterID());
			assertEquals("T", tpars[0].getName());
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			binding = (ICPPClassTemplate) findBinding("CT");
			assertEquals(ICPPClassType.k_class, binding.getKey());
			ICPPTemplateParameter[] tpars = binding.getTemplateParameters();
			assertEquals(1, tpars.length);
			assertTrue(tpars[0] instanceof ICPPTemplateTemplateParameter);
			assertEquals("V", tpars[0].getName());
			assertEquals(0, tpars[0].getParameterID());
			tpars = ((ICPPTemplateTemplateParameter) tpars[0]).getTemplateParameters();
			assertEquals(1, tpars.length);
			assertTrue(tpars[0] instanceof ICPPTemplateTemplateParameter);
			assertEquals(0x10000, tpars[0].getParameterID());
			assertEquals("T", tpars[0].getName());
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			binding = (ICPPClassTemplate) findBinding("CT");
			assertEquals(ICPPClassType.k_class, binding.getKey());
			ICPPTemplateParameter[] tpars = binding.getTemplateParameters();
			assertEquals(1, tpars.length);
			assertTrue(tpars[0] instanceof ICPPTemplateTypeParameter);
			assertEquals(0, tpars[0].getParameterID());
			assertEquals("U", tpars[0].getName());
			assertEquals(pdomid, ((IAdaptable) tpars[0]).getAdapter(PDOMNode.class).getBindingID());
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// int globalVar;

	// #include "header.h"
	// void test() {
	//    globalVar= 1;
	// }
	public void testChangingSourceBeforeHeader_Bug171834() throws Exception {
		CModelListener.sSuppressUpdateOfLastRecentlyUsed = false;
		setupHeader(2, true);
		setupFile(0, true);
		IBinding binding;
		ICompositeType ct;
		fIndex.acquireReadLock();
		try {
			binding = findBinding("globalVar");
			assertTrue(binding instanceof IVariable);
			assertEquals(2, fIndex.findNames(binding, IIndex.FIND_ALL_OCCURRENCES).length);
		} finally {
			fIndex.releaseReadLock();
		}

		fFile = TestSourceReader.createFile(fFile.getParent(), fFile.getName(),
				fContents[1].toString().replaceAll("globalVar", "newVar"));
		TestSourceReader.waitUntilFileIsIndexed(fIndex, fFile, INDEXER_TIMEOUT_MILLISEC);

		fIndex.acquireReadLock();
		try {
			binding = findBinding("globalVar");
			assertTrue(binding instanceof IVariable);
			assertEquals(1, fIndex.findNames(binding, IIndex.FIND_ALL_OCCURRENCES).length);
		} finally {
			fIndex.releaseReadLock();
		}

		fHeader = TestSourceReader.createFile(fHeader.getParent(), fHeader.getName(),
				fContents[0].toString().replaceAll("globalVar", "newVar"));
		waitUntilFileIsIndexed(fIndex, fHeader);

		fIndex.acquireReadLock();
		try {
			binding = findBinding("newVar");
			assertTrue(binding instanceof IVariable);
			assertEquals(2, fIndex.findNames(binding, IIndex.FIND_ALL_OCCURRENCES).length);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// int globalVar;
	// void func();

	// extern "C" {
	//    int globalVar;
	//    void func();
	// }

	// int globalVar;
	// void func();

	// extern "C" int globalVar;
	// extern "C" void func();

	// int globalVar;
	// void func();
	public void testExternC() throws Exception {
		setupFile(5, true);
		checkExternC(false);
		updateFile();
		checkExternC(true);
		updateFile();
		checkExternC(false);
		updateFile();
		checkExternC(true);
		updateFile();
		checkExternC(false);
	}

	private void checkExternC(boolean value) throws Exception {
		fIndex.acquireReadLock();
		try {
			ICPPVariable var = (ICPPVariable) findBinding("globalVar");
			assertEquals(value, var.isExternC());
			ICPPFunction func = (ICPPFunction) findBinding("func");
			assertEquals(value, func.isExternC());
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// int global;
	// struct C {int mem;};
	// enum E {e0};

	// int global=1;
	// struct C {int mem=1;};
	// enum E {e0=1};

	// int global;
	// struct C {int mem;};
	// enum E {e0};
	public void testValuesC() throws Exception {
		setupFile(3, false);
		checkValue("global", null);
		checkValue("C::mem", null);
		checkValue("e0", 0L);
		updateFile();
		checkValue("global", 1L);
		checkValue("C::mem", 1L);
		checkValue("e0", 1L);
		updateFile();
		checkValue("global", null);
		checkValue("C::mem", null);
		checkValue("e0", 0L);
	}

	// class A {
	//    public: void foo();
	// };

	// class A {
	//    public: void foo() throw();
	// };

	// class A {
	//    public: void foo() throw(int, double);
	// };

	// class A {
	//    public: void foo() throw();
	// };

	// class A {
	//    public: void foo();
	// };
	public void testExceptionSpecification() throws Exception {
		ICPPMethod method;
		IType[] exceptionSpec;
		setupFile(5, true);
		fIndex.acquireReadLock();
		try {
			method = (ICPPMethod) findBinding("A::foo");
			exceptionSpec = method.getExceptionSpecification();
			assertNull(exceptionSpec);
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			method = (ICPPMethod) findBinding("A::foo");
			exceptionSpec = method.getExceptionSpecification();
			assertEquals(0, exceptionSpec.length);
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			method = (ICPPMethod) findBinding("A::foo");
			exceptionSpec = method.getExceptionSpecification();
			assertNotNull(exceptionSpec);
			assertEquals(2, exceptionSpec.length);
			assertEquals("int", ASTTypeUtil.getType(exceptionSpec[0]));
			assertEquals("double", ASTTypeUtil.getType(exceptionSpec[1]));
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			method = (ICPPMethod) findBinding("A::foo");
			exceptionSpec = method.getExceptionSpecification();
			assertEquals(0, exceptionSpec.length);
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			method = (ICPPMethod) findBinding("A::foo");
			exceptionSpec = method.getExceptionSpecification();
			assertNull(exceptionSpec);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// int global;
	// struct C {int mem;};
	// enum E {e0};

	// int global=1;
	// struct C {int mem=1;};
	// enum E {e0=1};

	// int global;
	// struct C {int mem;};
	// enum E {e0};
	public void testValuesCPP() throws Exception {
		setupFile(3, true);
		checkValue("global", null);
		checkValue("C::mem", null);
		checkValue("e0", 0L);
		updateFile();
		checkValue("global", 1L);
		checkValue("C::mem", 1L);
		checkValue("e0", 1L);
		updateFile();
		checkValue("global", null);
		checkValue("C::mem", null);
		checkValue("e0", 0L);
	}

	//class A {};
	//class B {friend class A;};

	//class B {};
	public void testFriendClass() throws Exception {
		setupFile(2, true);
		assertFriendRemoval("B", "A");
	}

	// class X {public: char* foo(int);};
	// class Y {friend char* X::foo(int);};

	// class Y {};
	public void testFriendMethod() throws Exception {
		setupFile(2, true);
		assertFriendRemoval("Y", "X::foo");
	}

	// class X {friend void friend_set(X*, int);};
	// void friend_set(X* p, int i) {}

	// class X {};
	public void testFriendFunction() throws Exception {
		setupFile(2, true);
		assertFriendRemoval("X", "friend_set");
	}

	private void assertFriendRemoval(String clientClassBinding, String supplierBinding) throws Exception {
		fIndex.acquireReadLock();
		try {
			IBinding client = findBinding(clientClassBinding);
			IBinding supplier = findBinding(supplierBinding);
			assertNotNull("Unable to find binding with name \"" + clientClassBinding + "\"", client);
			assertTrue("Unable to find binding with name \"" + clientClassBinding + "\"",
					client instanceof ICPPClassType);
			assertNotNull("Unable to find binding with name \"" + supplierBinding + "\"", supplier);
			assertTrue(((ICPPClassType) client).getFriends().length == 1);
			assertTrue(((ICPPClassType) client).getFriends()[0].equals(supplier));
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();

		fIndex.acquireReadLock();
		try {
			IBinding client = findBinding(clientClassBinding);
			assertNotNull("Unable to find binding with name \"" + clientClassBinding + "\"", client);
			assertTrue("Unable to find binding with name \"" + clientClassBinding + "\"",
					client instanceof ICPPClassType);
			assertTrue(((ICPPClassType) client).getFriends().length == 0);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// void funcTypeDeletion(int);

	// #include "header.h"
	// typeof(funcTypeDeletion) storeRef;
	// char funcTypeDeletion(int);		// delete type
	// typeof(storeRef) useRef;         // use reference
	public void testTypedeletion_Bug294306() throws Exception {
		setupHeader(2, true);
		setupFile(2, true);
		checkFunction("useRef", new String[] { "void", "int" }, new String[] {});
		fContentUsed--;
		updateFile();
		checkFunction("useRef", new String[] { "char", "int" }, new String[] {});
	}

	// void f(int a, int b=0);

	// #include "header.h"
	// void f(int a, int b) {}
	// void ref() {
	//   f(1);
	// }

	// #include "header.h"
	// void f(int a, int b) {}
	// void ref() {
	//   f(1);
	// }
	public void testDefaultParam_Bug297438() throws Exception {
		setupHeader(3, true);
		setupFile(3, true);
		checkReferenceCount("f", 1);
		updateFile();
		checkReferenceCount("f", 1);
	}

	private void checkReferenceCount(String name, int count) throws InterruptedException, CoreException {
		fIndex.acquireReadLock();
		try {
			IBinding func = findBinding(name);
			assertEquals(count, fIndex.findReferences(func).length);
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// enum E {e0};

	// enum class E;

	// enum E : short {e1};

	// enum class E {e2};

	// enum class E : short {e1};

	// enum E : int;
	public void testEnumCPP() throws Exception {
		setupFile(6, true);
		checkEnum(false, null, "e0");
		updateFile();
		checkEnum(true, "int", null);
		updateFile();
		checkEnum(false, "short int", "e1");
		updateFile();
		checkEnum(true, "int", "e2");
		updateFile();
		checkEnum(true, "short int", "e1");
		updateFile();
		checkEnum(false, "int", null);
	}

	private void checkEnum(boolean scoped, String fixedType, String enumItem) throws Exception {
		fIndex.acquireReadLock();
		try {
			ICPPEnumeration enumType = (ICPPEnumeration) findBinding("E");
			assertEquals(scoped, enumType.isScoped());
			if (fixedType == null) {
				assertNull(enumType.getFixedType());
			} else {
				assertEquals(fixedType, ASTTypeUtil.getType(enumType.getFixedType()));
			}
			final IEnumerator[] enumItems = enumType.getEnumerators();
			if (enumItem == null) {
				assertEquals(0, enumItems.length);
			} else {
				assertEquals(1, enumItems.length);
				assertEquals(enumItem, enumItems[0].getName());
			}
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// class X {};

	// class X {};
	public void testFileLocalBinding() throws Exception {
		setupFile(2, true);
		long id1, id2;
		fIndex.acquireReadLock();
		try {
			final IIndexBinding binding = findBinding("X");
			id1 = ((PDOMFile) binding.getLocalToFile()).getRecord();
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			final IIndexBinding binding = findBinding("X");
			id2 = ((PDOMFile) binding.getLocalToFile()).getRecord();
		} finally {
			fIndex.releaseReadLock();
		}
		assertEquals(id1, id2);
	}

	// namespace ns {
	//    namespace m {}
	// }

	// inline namespace ns {
	//    inline namespace m {}
	// }

	// namespace ns {
	//    namespace m {}
	// }
	public void testInlineNamespaces_305980() throws Exception {
		setupFile(3, true);
		fIndex.acquireReadLock();
		try {
			final ICPPNamespace ns = (ICPPNamespace) findBinding("ns");
			assertFalse(ns.isInline());
			final ICPPNamespace m = (ICPPNamespace) findBinding("ns::m");
			assertFalse(ns.isInline());
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			final ICPPNamespace ns = (ICPPNamespace) findBinding("ns");
			assertTrue(ns.isInline());
			final ICPPNamespace m = (ICPPNamespace) findBinding("ns::m");
			assertTrue(ns.isInline());
		} finally {
			fIndex.releaseReadLock();
		}

		updateFile();
		fIndex.acquireReadLock();
		try {
			final ICPPNamespace ns = (ICPPNamespace) findBinding("ns");
			assertFalse(ns.isInline());
			final ICPPNamespace m = (ICPPNamespace) findBinding("ns::m");
			assertFalse(ns.isInline());
		} finally {
			fIndex.releaseReadLock();
		}
	}

	//	typedef enum {
	//		AE_ON = 0
	//	} Adaptiv_T;
	//	struct mystruct {
	//		Adaptiv_T       eAdapt;
	//	};

	// int main() {
	//    mystruct ms;
	//    ms.eAdapt = AE_ON;
	// }

	//  // insert line
	//	typedef enum {
	//		AE_ON = 0
	//	} Adaptiv_T;
	//	struct mystruct {
	//		Adaptiv_T       eAdapt;
	//	};
	public void testAnonymousEnum_Bug356057cpp() throws Exception {
		setupHeader(3, true);
		setupFile(3, true);
		String name1;
		fIndex.acquireReadLock();
		try {
			final IEnumerator e = (IEnumerator) findBinding("AE_ON");
			assertNotNull(e);
			name1 = e.getOwner().getName();
		} finally {
			fIndex.releaseReadLock();
		}
		updateHeader();
		fIndex.acquireReadLock();
		try {
			final IEnumerator e = (IEnumerator) findBinding("AE_ON");
			assertNotNull(e);
			assertFalse(name1.equals(e.getOwner().getName()));
		} finally {
			fIndex.releaseReadLock();
		}
	}

	//	typedef enum {
	//		AE_ON = 0
	//	} Adaptiv_T;
	//	struct mystruct {
	//		Adaptiv_T       eAdapt;
	//	};

	// int main() {
	//    mystruct ms;
	//    ms.eAdapt = AE_ON;
	// }

	//  // insert line
	//	typedef enum {
	//		AE_ON = 0
	//	} Adaptiv_T;
	//	struct mystruct {
	//		Adaptiv_T       eAdapt;
	//	};
	public void testAnonymousEnum_Bug356057c() throws Exception {
		setupHeader(3, false);
		setupFile(3, false);
		String name1;
		fIndex.acquireReadLock();
		try {
			final IEnumerator e = (IEnumerator) findBinding("AE_ON");
			assertNotNull(e);
			name1 = e.getOwner().getName();
		} finally {
			fIndex.releaseReadLock();
		}
		updateHeader();
		fIndex.acquireReadLock();
		try {
			final IEnumerator e = (IEnumerator) findBinding("AE_ON");
			assertNotNull(e);
			assertFalse(name1.equals(e.getOwner().getName()));
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// struct S {};

	// struct S {S(int){}};
	public void testImplicitDefaultCtor_Bug359376() throws Exception {
		setupFile(2, true);
		fIndex.acquireReadLock();
		try {
			final ICPPClassType s = (ICPPClassType) findBinding("S");
			assertNotNull(s);
			final ICPPConstructor[] ctors = s.getConstructors();
			assertEquals(2, ctors.length); // 2 implicit ctors
			assertTrue(ctors[0].isImplicit());
			assertTrue(ctors[1].isImplicit());
		} finally {
			fIndex.releaseReadLock();
		}
		updateFile();

		fIndex.acquireReadLock();
		try {
			final ICPPClassType s = (ICPPClassType) findBinding("S");
			assertNotNull(s);
			final ICPPConstructor[] ctors = s.getConstructors();
			assertEquals(2, ctors.length); // 1 explicit and one implicit ctor
			assertTrue(ctors[0].isImplicit() != ctors[1].isImplicit());
		} finally {
			fIndex.releaseReadLock();
		}
	}

	//	struct Base {
	//	    void foo() {}
	//	};
	//	struct Derived: Base {
	//		Derived();
	//	};

	//	struct Base {
	//	    void foo() {}
	//	};
	//	struct Derived: Base {
	//		Derived();
	//	};
	public void testBaseClass_Bug391284() throws Exception {
		setupFile(2, true);
		fIndex.acquireReadLock();
		try {
			final ICPPClassType s = (ICPPClassType) findBinding("Derived");
			assertNotNull(s);
			final ICPPBase[] bases = s.getBases();
			assertEquals(1, bases.length);
			assertEquals("Base", bases[0].getBaseClass().getName());
		} finally {
			fIndex.releaseReadLock();
		}
		updateFile();

		fIndex.acquireReadLock();
		try {
			final ICPPClassType s = (ICPPClassType) findBinding("Derived");
			assertNotNull(s);
			final ICPPBase[] bases = s.getBases();
			assertEquals(1, bases.length);
			assertEquals("Base", bases[0].getBaseClass().getName());
		} finally {
			fIndex.releaseReadLock();
		}
	}

	// int dummy;

	//#include "A.h"
	//void foo() {
	//  bar();
	//}
	public void testDependentProjectGetsUpdated_Bug310837() throws Exception {
		CharSequence[] contents = getContentsForTest(2);
		List<ICProject> projects = new ArrayList<>();

		try {
			ProjectBuilder projectABuilder = new ProjectBuilder("projA_" + getName(), true);
			projectABuilder.addFile("A.h", contents[0]);
			ICProject projectA = projectABuilder.create();
			projects.add(projectA);

			ProjectBuilder projectBBuilder = new ProjectBuilder("projB_" + getName(), true);
			projectBBuilder.addFile("B.h", contents[1]).addDependency(projectA.getProject());
			ICProject projectB = projectBBuilder.create();
			projects.add(projectB);

			IIndex aIndex = CCorePlugin.getIndexManager().getIndex(projectA);
			IIndex bIndex = CCorePlugin.getIndexManager().getIndex(projectB);
			bIndex.acquireReadLock();
			IIndexBinding[] barBinding = bIndex.findBindings("bar".toCharArray(), IndexFilter.ALL, null);
			assertEquals(0, barBinding.length);
			bIndex.releaseReadLock();

			IFile fileAh = (IFile) ((ITranslationUnit) projectA.findElement(Path.fromOSString("A.h"))).getResource();
			fileAh = TestSourceReader.createFile(projectA.getSourceRoots()[0].getResource(), Path.fromOSString("A.h"),
					"void bar(){}\n");
			TestSourceReader.waitUntilFileIsIndexed(aIndex, fileAh, INDEXER_TIMEOUT_SEC * 1000);
			TestSourceReader.waitUntilFileIsIndexed(bIndex, fileAh, INDEXER_TIMEOUT_SEC * 1000);
			bIndex.acquireReadLock();
			IIndexBinding[] barBinding2 = bIndex.findBindings("bar".toCharArray(), IndexFilter.ALL, null);
			assertEquals(1, barBinding2.length);
			bIndex.releaseReadLock();
		} finally {
			for (ICProject project : projects) {
				CProjectHelper.delete(project);
			}
		}
	}
}
