/******************************************************************************* * Copyright (c) 2006, 2010 Wind River Systems, Inc. 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: * Markus Schorn - initial API and implementation * Andrew Ferguson (Symbian) *******************************************************************************/ package org.eclipse.cdt.core.testplugin.util; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import junit.framework.Assert; import junit.framework.TestCase; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.dom.ILinkage; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.index.IIndex; import org.eclipse.cdt.core.index.IIndexFile; import org.eclipse.cdt.core.index.IndexLocationFactory; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.osgi.framework.Bundle; /** * Utilities for reading test source code from plug-in .java sources */ public class TestSourceReader { /** * Returns an array of StringBuilder objects for each comment section found preceding the named * test in the source code. * @param bundle the bundle containing the source, if null can try to load using classpath * (source folder has to be in the classpath for this to work) * @param srcRoot the directory inside the bundle containing the packages * @param clazz the name of the class containing the test * @param testName the name of the test * @param sections the number of comment sections preceding the named test to return. Pass zero * to get all available sections. * @return an array of StringBuilder objects for each comment section found preceding the named * test in the source code. * @throws IOException */ public static StringBuilder[] getContentsForTest(Bundle bundle, String srcRoot, Class clazz, final String testName, int sections) throws IOException { String fqn = clazz.getName().replace('.', '/'); fqn = fqn.indexOf("$") == -1 ? fqn : fqn.substring(0, fqn.indexOf("$")); String classFile = fqn + ".java"; IPath filePath= new Path(srcRoot + '/' + classFile); InputStream in; try { if (bundle != null) { in = FileLocator.openStream(bundle, filePath, false); } else { in = clazz.getResourceAsStream('/' + classFile); } } catch (IOException e) { if (clazz.getSuperclass() != null && !clazz.equals(TestCase.class)) { return getContentsForTest(bundle, srcRoot, clazz.getSuperclass(), testName, sections); } throw e; } BufferedReader br = new BufferedReader(new InputStreamReader(in)); List<StringBuilder> contents = new ArrayList<StringBuilder>(); StringBuilder content = new StringBuilder(); for (String line = br.readLine(); line != null; line = br.readLine()) { line = line.replaceFirst("^\\s*", ""); // replace leading whitespace, preserve trailing if (line.startsWith("//")) { content.append(line.substring(2) + "\n"); } else { if (content.length() > 0) { contents.add(content); if (sections > 0 && contents.size() == sections + 1) contents.remove(0); content = new StringBuilder(); } int idx= line.indexOf(testName); if (idx != -1 && !Character.isJavaIdentifierPart(line.charAt(idx + testName.length()))) { return contents.toArray(new StringBuilder[contents.size()]); } } } if (clazz.getSuperclass() != null && !clazz.equals(TestCase.class)) { return getContentsForTest(bundle, srcRoot, clazz.getSuperclass(), testName, sections); } throw new IOException("Test data not found for " + clazz + " " + testName); } /** * Searches for the offset of the first occurrence of a string in a workspace file. * @param lookfor string to be searched for * @param fullPath full path of the workspace file * @return the offset or -1 * @throws Exception * @throws UnsupportedEncodingException * @since 4.0 */ public static int indexOfInFile(String lookfor, Path fullPath) throws Exception { IFile file= ResourcesPlugin.getWorkspace().getRoot().getFile(fullPath); Reader reader= new BufferedReader(new InputStreamReader(file.getContents(), file.getCharset())); Assert.assertTrue(lookfor.indexOf('\n') == -1); try { int c= 0; int offset= 0; StringBuilder buf= new StringBuilder(); while ((c = reader.read()) >= 0) { buf.append((char) c); if (c == '\n') { int idx= buf.indexOf(lookfor); if (idx >= 0) { return idx + offset; } offset += buf.length(); buf.setLength(0); } } int idx= buf.indexOf(lookfor); if (idx >= 0) { return idx + offset; } return -1; } finally { reader.close(); } } public static int getLineNumber(int offset, Path fullPath) throws Exception { IFile file= ResourcesPlugin.getWorkspace().getRoot().getFile(fullPath); Reader reader= new BufferedReader(new InputStreamReader(file.getContents(), file.getCharset())); try { int line = 1; for (int i = 0; i < offset; i++) { int c= reader.read(); Assert.assertTrue(c >= 0); if (c == '\n') line++; } return line; } finally { reader.close(); } } /** * Reads a section in comments form the source of the given class. The section * is started with '// {tag}' and ends with the first line not started by '//' * @since 4.0 */ public static String readTaggedComment(Bundle bundle, String srcRoot, Class clazz, final String tag) throws IOException { IPath filePath= new Path(srcRoot + '/' + clazz.getName().replace('.', '/') + ".java"); InputStream in= FileLocator.openStream(bundle, filePath, false); LineNumberReader reader= new LineNumberReader(new InputStreamReader(in)); boolean found= false; final StringBuilder content= new StringBuilder(); try { String line= reader.readLine(); while (line != null) { line= line.trim(); if (line.startsWith("//")) { line= line.substring(2); if (found) { content.append(line); content.append('\n'); } else { line= line.trim(); if (line.startsWith("{" + tag)) { if (line.length() == tag.length() + 1 || !Character.isJavaIdentifierPart(line.charAt(tag.length() + 1))) { found= true; } } } } else if (found) { break; } line= reader.readLine(); } } finally { reader.close(); } Assert.assertTrue("Tag '" + tag + "' is not defined inside of '" + filePath + "'.", found); return content.toString(); } /** * Creates a file with content at the given path inside the given container. * If the file exists its content is replaced. * @param container a container to create the file in * @param filePath the path relative to the container to create the file at * @param contents the content for the file * @return a file object. * @throws CoreException * @since 4.0 */ public static IFile createFile(final IContainer container, final IPath filePath, final String contents) throws CoreException { final IWorkspace ws = ResourcesPlugin.getWorkspace(); final IFile result[] = new IFile[1]; ws.run(new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { //Obtain file handle IFile file = container.getFile(filePath); InputStream stream = new ByteArrayInputStream(contents.getBytes()); //Create file input stream if (file.exists()) { long timestamp= file.getLocalTimeStamp(); file.setContents(stream, false, false, new NullProgressMonitor()); if (file.getLocalTimeStamp() == timestamp) { file.setLocalTimeStamp(timestamp + 1000); } } else { createFolders(file); file.create(stream, true, new NullProgressMonitor()); } result[0]= file; } private void createFolders(IResource res) throws CoreException { IContainer container= res.getParent(); if (!container.exists() && container instanceof IFolder) { createFolders(container); ((IFolder) container).create(true, true, new NullProgressMonitor()); } } }, null); return result[0]; } /** * Creates a file with content at the given path inside the given container. * If the file exists its content is replaced. * @param container a container to create the file in * @param filePath the path relative to the container to create the file at * @param contents the content for the file * @return a file object. * @since 4.0 */ public static IFile createFile(IContainer container, String filePath, String contents) throws CoreException { return createFile(container, new Path(filePath), contents); } /** * Waits until the given file is indexed. Fails if this does not happen within the * given time. * @param file * @param maxmillis * @throws Exception * @since 4.0 */ public static void waitUntilFileIsIndexed(IIndex index, IFile file, int maxmillis) throws Exception { long endTime= System.currentTimeMillis() + maxmillis; int timeLeft= maxmillis; while (timeLeft >= 0) { Assert.assertTrue(CCorePlugin.getIndexManager().joinIndexer(timeLeft, new NullProgressMonitor())); index.acquireReadLock(); try { IIndexFile[] files= index.getFiles(ILinkage.CPP_LINKAGE_ID, IndexLocationFactory.getWorkspaceIFL(file)); if (files.length > 0 && areAllFilesNotOlderThan(files, file.getLocalTimeStamp())) { return; } files= index.getFiles(ILinkage.C_LINKAGE_ID, IndexLocationFactory.getWorkspaceIFL(file)); if (files.length > 0 && areAllFilesNotOlderThan(files, file.getLocalTimeStamp())) { return; } } finally { index.releaseReadLock(); } Thread.sleep(50); timeLeft= (int) (endTime - System.currentTimeMillis()); } Assert.fail("Indexing " + file.getFullPath() + " did not complete in time!"); } private static boolean areAllFilesNotOlderThan(IIndexFile[] files, long timestamp) throws CoreException { for (IIndexFile file : files) { if (file.getTimestamp() < timestamp) { return false; } } return true; } public static IASTTranslationUnit createIndexBasedAST(IIndex index, ICProject project, IFile file) throws CModelException, CoreException { ICElement elem= project.findElement(file.getFullPath()); if (elem instanceof ITranslationUnit) { ITranslationUnit tu= (ITranslationUnit) elem; return tu.getAST(index, ITranslationUnit.AST_SKIP_INDEXED_HEADERS); } Assert.fail("Could not create ast for " + file.getFullPath()); return null; } }