/******************************************************************************* * Copyright (c) 2013, 2014 QNX 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 Eidsness - Initial implementation *******************************************************************************/ package org.eclipse.cdt.internal.pdom.tests; import junit.framework.Test; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.dom.IPDOMManager; 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.testplugin.CProjectHelper; import org.eclipse.cdt.core.testplugin.util.BaseTestCase; import org.eclipse.cdt.core.testplugin.util.TestSourceReader; import org.eclipse.cdt.internal.core.CCoreInternals; import org.eclipse.cdt.internal.core.pdom.PDOM; import org.eclipse.cdt.internal.core.pdom.dom.IPDOMIterator; import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding; import org.eclipse.cdt.internal.core.pdom.dom.PDOMName; import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.NullProgressMonitor; public class PDOMNameTests extends BaseTestCase { private ICProject cproject; public static Test suite() { return suite(PDOMNameTests.class); } @Override protected void setUp() throws Exception { super.setUp(); cproject= CProjectHelper.createCCProject("PDOMNameTest" + System.currentTimeMillis(), "bin", IPDOMManager.ID_FAST_INDEXER); waitForIndexer(cproject); } @Override protected void tearDown() throws Exception { if (cproject != null) { cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, new NullProgressMonitor()); } super.tearDown(); } public void testExternalReferences() throws Exception { IProject project = cproject.getProject(); // Use enum because this uses a different NodeType in C++ and C. TestSourceReader.createFile(project, "file.cpp", "enum E_cpp { e_cpp }; extern E_cpp func_cpp() { func_cpp(); return e_cpp; }"); TestSourceReader.createFile(project, "file.c", "enum E_c { e_c }; extern enum E_c func_c() { func_c(); return e_c; }"); IndexerPreferences.set(project, IndexerPreferences.KEY_INDEXER_ID, IPDOMManager.ID_FAST_INDEXER); CCorePlugin.getIndexManager().reindex(cproject); waitForIndexer(cproject); PDOM pdom = (PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject); pdom.acquireWriteLock(null); try { IIndexBinding[] bindings = pdom.findBindings(new char[][]{"E_cpp".toCharArray()}, IndexFilter.ALL, npm()); assertEquals(1, bindings.length); assertTrue(bindings[0] instanceof PDOMBinding); PDOMBinding binding_cpp = (PDOMBinding) bindings[0]; PDOMName name_cpp = binding_cpp.getFirstReference(); assertNotNull(name_cpp); assertSame(binding_cpp.getLinkage(), name_cpp.getLinkage()); bindings = pdom.findBindings(new char[][]{"E_c".toCharArray()}, IndexFilter.ALL, npm()); assertEquals(1, bindings.length); assertTrue(bindings[0] instanceof PDOMBinding); PDOMBinding binding_c = (PDOMBinding) bindings[0]; PDOMName name_c1 = binding_c.getFirstReference(); assertNotNull(name_c1); assertSame(binding_c.getLinkage(), name_c1.getLinkage()); // Check that the external references list is currently empty. assertEquals(0, countExternalReferences(binding_cpp)); // Make sure the C++ binding and the C name are in different linkages, then add the name // as an external reference of the binding. The case we're setting up is: // // C++_Binding is referenced-by a C_Name which has a C++_Binding // // We can then test the following (see reference numbers below): // 1) Getting the C name as an external reference of the C++ binding // 2) Loading the C++ binding from the C name assertNotSame(binding_cpp.getLinkage(), name_c1.getLinkage()); name_c1.setBinding(binding_cpp); binding_cpp.addReference(name_c1); // Make sure there is an external reference, then retrieve it. Then make sure there // aren't anymore external references. IPDOMIterator<PDOMName> extNames = binding_cpp.getExternalReferences(); assertNotNull(extNames); assertTrue(extNames.hasNext()); PDOMName extRef = extNames.next(); assertNotNull(extRef); assertFalse(extNames.hasNext()); // 1) Check that the external reference is the same as the C name that was added, that the // external reference does not have the same linkage as the binding, and that it does // have the same linkage as the initial name. assertSame(name_c1.getLinkage(), extRef.getLinkage()); assertEquals(name_c1.getRecord(), extRef.getRecord()); assertNotSame(binding_cpp.getLinkage(), extRef.getLinkage()); assertSame(binding_c.getLinkage(), extRef.getLinkage()); // 2) Make sure that the C name was able to properly load the C++ binding. PDOMBinding extBinding = extRef.getBinding(); assertNotNull(extBinding); assertEquals(binding_cpp.getRecord(), extBinding.getRecord()); assertEquals(binding_cpp.getNodeType(), extBinding.getNodeType()); assertTrue(binding_cpp.getClass() == extBinding.getClass()); // Bug 426238: When names are deleted they need to be removed from the external references // list. This test puts 3 names into the list and then removes the 2nd, last, // and then first. // Add more external references and then check removals. PDOMName name_c2 = name_c1.getNextInFile(); assertNotNull(name_c2); PDOMName name_c3 = name_c2.getNextInFile(); name_c2.setBinding(binding_cpp); binding_cpp.addReference(name_c2); name_c3.setBinding(binding_cpp); binding_cpp.addReference(name_c3); // Collect the 3 names from the external reference list. extNames = binding_cpp.getExternalReferences(); assertNotNull(extNames); assertTrue(extNames.hasNext()); PDOMName extRef_1 = extNames.next(); assertNotNull(extRef); assertTrue(extNames.hasNext()); PDOMName extRef_2 = extNames.next(); assertTrue(extNames.hasNext()); PDOMName extRef_3 = extNames.next(); assertFalse(extNames.hasNext()); // Check that the list is ordered as expected (reversed during insertion). assertEquals(name_c3.getRecord(), extRef_1.getRecord()); assertEquals(name_c2.getRecord(), extRef_2.getRecord()); assertEquals(name_c1.getRecord(), extRef_3.getRecord()); // 3) Check deleting of middle entry. extRef_2.delete(); assertEquals(2, countExternalReferences(binding_cpp)); // 4) Make sure extref head is updated when there is a next name. extRef_1.delete(); assertEquals(1, countExternalReferences(binding_cpp)); extNames = binding_cpp.getExternalReferences(); assertNotNull(extNames); assertTrue(extNames.hasNext()); assertEquals(extRef_3.getRecord(), extNames.next().getRecord()); assertFalse(extNames.hasNext()); // 5) Check deleting of first entry. extRef_3.delete(); assertEquals(0, countExternalReferences(binding_cpp)); } finally { pdom.releaseWriteLock(); } } private static int countExternalReferences(PDOMBinding binding) throws Exception { IPDOMIterator<PDOMName> extRefs = binding.getExternalReferences(); assertNotNull(extRefs); int count = 0; for( ; extRefs.hasNext(); extRefs.next()) ++count; return count; } }