/*******************************************************************************
 * Copyright (c) 2007, 2014 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
 *     Markus Schorn (Wind River Systems)
 *     Sergey Prigogin (Google)
 *******************************************************************************/
package org.eclipse.cdt.internal.pdom.tests;

import java.io.File;
import java.util.regex.Pattern;

import junit.framework.Test;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IName;
import org.eclipse.cdt.core.dom.IPDOMManager;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexLocationConverter;
import org.eclipse.cdt.core.index.IndexFilter;
import org.eclipse.cdt.core.index.ResourceContainerRelativeLocationConverter;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.LanguageManager;
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.index.IIndexFragment;
import org.eclipse.cdt.internal.core.index.IWritableIndexFragment;
import org.eclipse.cdt.internal.core.pdom.PDOM;
import org.eclipse.cdt.internal.core.pdom.PDOMManager;
import org.eclipse.cdt.internal.core.pdom.WritablePDOM;
import org.eclipse.cdt.internal.core.pdom.db.ChunkCache;
import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences;
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.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;

/**
 * Tests bugs found in the PDOM.
 */
public class PDOMCPPBugsTest extends BaseTestCase {
	ICProject cproject;

	public static Test suite() {
		return suite(PDOMCPPBugsTest.class);
	}

	@Override
	protected void setUp() throws Exception {
		super.setUp();
		cproject= CProjectHelper.createCCProject("PDOMBugsTest" + 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 testPDOMProperties() throws Exception {
		PDOM pdom = (PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject);
		pdom.acquireWriteLock(0, null);
		try {
			WritablePDOM wpdom = (WritablePDOM) pdom;
			IIndexBinding[] b = wpdom.findBindings(Pattern.compile(".+"), false, IndexFilter.ALL, null);
			wpdom.setProperty("a", "b");
			assertEquals("b", wpdom.getProperty("a"));
			wpdom.setProperty("c", "d");
			assertEquals("d", wpdom.getProperty("c"));
			wpdom.setProperty("c", "e");
			assertEquals("e", wpdom.getProperty("c"));
		} finally {
			pdom.releaseWriteLock(0, true);
		}
	}

	public void testProjectPDOMProperties() throws Exception {
		PDOM pdom = (PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject);
		pdom.acquireReadLock();
		try {
			String id= pdom.getProperty(IIndexFragment.PROPERTY_FRAGMENT_ID);
			assertNotNull(id);

			CCoreInternals.getPDOMManager().reindex(cproject);

			String id2= pdom.getProperty(IIndexFragment.PROPERTY_FRAGMENT_ID);
			assertNotNull(id2);
			assertEquals(id, id2);
		} finally {
			pdom.releaseReadLock();
		}
	}

	public void testProjectPDOMPropertiesOnExport() throws Exception {
		// this test is currently failing on the cdt test build machine, but
		// not on my local linux or windows boxes.

		File tmp = nonExistentTempFile("temp", ".pdom");
		IIndexLocationConverter cvr= new ResourceContainerRelativeLocationConverter(cproject.getProject());
		final PDOMManager pdomManager = CCoreInternals.getPDOMManager();
		pdomManager.exportProjectPDOM(cproject, tmp, cvr, null);

		IWritableIndexFragment pdom = new WritablePDOM(tmp, cvr, new ChunkCache(), LanguageManager.getInstance().getPDOMLinkageFactoryMappings());
		pdom.acquireReadLock();
		try {
			String id= pdom.getProperty(IIndexFragment.PROPERTY_FRAGMENT_ID);
			assertNotNull("Exported pdom ID is null", id);

			String id2 = getFragmentID(cproject);
			assertNotNull("Project pdom ID is null", id2);
			assertFalse("Project pdom ID equals export PDOM id", id2.equals(id));

			pdomManager.reindex(cproject);
			waitForIndexer(cproject);

			String id3= pdom.getProperty(IIndexFragment.PROPERTY_FRAGMENT_ID);
			assertNotNull("Exported pdom ID is null after project reindex", id3);
			assertEquals("Exported pdom ID hasChanged during reindex", id, id3);

			String id4= getFragmentID(cproject);
			assertNotNull("Reindexed project pdom ID is null", id4);
			assertFalse("Reindexex project pdom ID equals exported pdom ID", id4.equals(id));
		} finally {
			pdom.releaseReadLock();
		}
	}

	private String getFragmentID(final ICProject cproject) throws CoreException, InterruptedException {
		PDOMManager pdomManager= CCoreInternals.getPDOMManager();
		final PDOM projectPDOM = (PDOM)pdomManager.getPDOM(cproject);
		String id2;
		projectPDOM.acquireReadLock();
		try {
			id2= (projectPDOM).getProperty(IIndexFragment.PROPERTY_FRAGMENT_ID);
		}
		finally {
			projectPDOM.releaseReadLock();
		}
		return id2;
	}

	public void testInterruptingAcquireReadLock() throws Exception {
		final PDOM pdom= (PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject);
		final boolean[] ok= {false};
		pdom.acquireWriteLock(null);
		try {
			Thread other= new Thread() {
				@Override
				public void run() {
					try {
						pdom.acquireReadLock();
					} catch (InterruptedException e) {
						ok[0]= true;
					}
				}
			};
			other.start();
			other.interrupt();
			other.join();
			assertTrue("thread was not interrupted", ok[0]);
		}
		finally {
			pdom.releaseWriteLock();
		}
		pdom.acquireWriteLock(null);
		pdom.releaseWriteLock();
	}

	public void testInterruptingAcquireWriteLock() throws Exception {
		final WritablePDOM pdom= (WritablePDOM) CCoreInternals.getPDOMManager().getPDOM(cproject);
		final boolean[] ok= {false};
		pdom.acquireReadLock();
		try {
			Thread other= new Thread() {
				@Override
				public void run() {
					try {
						pdom.acquireReadLock();
						pdom.acquireWriteLock(1, null);
					} catch (InterruptedException e) {
						ok[0]= true;
						pdom.releaseReadLock();
					}
				}
			};
			other.start();
			other.interrupt();
			other.join();
			assertTrue("thread was not interrupted", ok[0]);
		}
		finally {
			pdom.releaseReadLock();
		}
		pdom.acquireWriteLock(null);
		pdom.releaseWriteLock();
	}

	public void test191679() throws Exception {
		IProject project= cproject.getProject();
		IFolder cHeaders= cproject.getProject().getFolder("cHeaders");
		cHeaders.create(true, true, npm());
		LanguageManager lm= LanguageManager.getInstance();

		IFile cHeader= TestSourceReader.createFile(cHeaders, "cSource.c", "void foo(int i){}");
		IFile cppSource= TestSourceReader.createFile(cHeaders, "cppSource.cpp", "extern \"C\" void foo(int i); void ref() {foo(1);}");

		IndexerPreferences.set(project, IndexerPreferences.KEY_INDEXER_ID, IPDOMManager.ID_FAST_INDEXER);
		CCorePlugin.getIndexManager().reindex(cproject);
		waitForIndexer(cproject);

		final PDOM pdom= (PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject);
		pdom.acquireReadLock();
		try {
			{ // test reference to 'foo' was resolved correctly
				IIndexBinding[] ib= pdom.findBindings(new char[][]{"foo".toCharArray()}, IndexFilter.ALL, npm());
				assertEquals(2, ib.length);
				if (ib[0] instanceof ICPPBinding) {
					IIndexBinding h= ib[0]; ib[0]= ib[1]; ib[1]= h;
				}
				assertTrue(ib[0] instanceof IFunction);
				assertFalse(ib[0] instanceof ICPPBinding);

				assertTrue(ib[1] instanceof IFunction);
				assertTrue(ib[1] instanceof ICPPBinding);

				IName[] nms= pdom.findNames(ib[0], IIndexFragment.FIND_REFERENCES | IIndexFragment.SEARCH_ACROSS_LANGUAGE_BOUNDARIES);
				assertEquals(1, nms.length);
				assertTrue(nms[0].getFileLocation().getFileName().endsWith(".cpp"));

				nms= pdom.findNames(ib[0], IIndexFragment.FIND_REFERENCES);
				assertEquals(0, nms.length);

				nms= pdom.findNames(ib[1], IIndexFragment.FIND_DEFINITIONS | IIndexFragment.SEARCH_ACROSS_LANGUAGE_BOUNDARIES);
				assertEquals(1, nms.length);
				assertTrue(nms[0].getFileLocation().getFileName().endsWith(".c"));

				nms= pdom.findNames(ib[1], IIndexFragment.FIND_DEFINITIONS);
				assertEquals(0, nms.length);
			}
		} finally {
			pdom.releaseReadLock();
		}
	}
}

