project/com.nokia.carbide.cpp.epoc.engine.tests/src/com/nokia/carbide/cpp/epoc/engine/tests/BaseTest.java
Keeping PlatSim internal only.
/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.carbide.cpp.epoc.engine.tests;
import com.nokia.carbide.cpp.epoc.engine.DocumentFactory;
import com.nokia.carbide.cpp.epoc.engine.model.IOwnedModel;
import com.nokia.carbide.cpp.epoc.engine.model.IView;
import com.nokia.carbide.cpp.epoc.engine.model.IViewParserConfiguration;
import com.nokia.carbide.internal.api.cpp.epoc.engine.dom.ASTFactory;
import com.nokia.carbide.internal.api.cpp.epoc.engine.dom.IASTNode;
import com.nokia.carbide.internal.api.cpp.epoc.engine.dom.IASTPreprocessorTokenStream;
import com.nokia.carbide.internal.api.cpp.epoc.engine.dom.IASTProblemNode;
import com.nokia.carbide.internal.api.cpp.epoc.engine.dom.IASTTopLevelNode;
import com.nokia.carbide.internal.api.cpp.epoc.engine.dom.IASTTranslationUnit;
import com.nokia.carbide.internal.api.cpp.epoc.engine.dom.IASTVisitor;
import com.nokia.carbide.internal.api.cpp.epoc.engine.dom.IDocumentSourceRegion;
import com.nokia.carbide.internal.api.cpp.epoc.engine.dom.ISourceRegion;
import com.nokia.carbide.internal.cpp.epoc.engine.dom.ASTUtils;
import com.nokia.carbide.internal.cpp.epoc.engine.dom.SourceLocationVisitorAdapter;
import com.nokia.carbide.internal.cpp.epoc.engine.model.ViewASTBase;
import com.nokia.carbide.internal.cpp.epoc.engine.parser.IDocumentParser;
import com.nokia.carbide.internal.cpp.epoc.engine.parser.ParserFactory;
import com.nokia.carbide.internal.cpp.epoc.engine.parser.ProblemVisitor;
import com.nokia.carbide.internal.cpp.epoc.engine.preprocessor.ASTPreprocessor;
import com.nokia.carbide.internal.cpp.epoc.engine.preprocessor.IConditionalBlock;
import com.nokia.cpp.internal.api.utils.core.FileUtils;
import com.nokia.cpp.internal.api.utils.core.HostOS;
import com.nokia.cpp.internal.api.utils.core.IMessage;
import com.nokia.cpp.internal.api.utils.core.TextUtils;
import org.eclipse.core.resources.WorkspaceJob;
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.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.osgi.framework.Bundle;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import junit.framework.TestCase;
public abstract class BaseTest extends TestCase {
public class DocSourceLocationVisitor extends SourceLocationVisitorAdapter {
StringBuilder builder;
private NodeVisitor nodeVisitor;
private IASTNode node;
/**
* @param expectFullSourceInfo
* @param refText
*/
public DocSourceLocationVisitor(NodeVisitor nodeVisitor) {
this.builder = nodeVisitor.builder;
this.nodeVisitor = nodeVisitor;
this.node = null;
}
public int visit(IDocumentSourceRegion region) {
for (ISourceRegion loc : nodeVisitor.regions) {
if (loc == region) {
builder.append("duplicate location in tree: " + node + "\n");
}
}
nodeVisitor.regions.add(region);
try {
String content;
content = region.getDocument().get(region.getRegion().getOffset(), region.getRegion().getLength());
if (!nodeVisitor.expectMacros) {
String ref = node.getNewText();
if (!compareSource(ref, content))
builder.append("source looks suspicious: expected '" + ref + "' but got '" + content+"'\n");
}
} catch (BadLocationException e) {
builder.append("cannot resolve source at " + region.getRegion() + " for " +node+"\n");
}
return VISIT_CHILDREN;
}
}
public class NodeVisitor implements IASTVisitor {
public IASTNode currentNode;
List<ISourceRegion> regions;
StringBuilder builder = new StringBuilder();
boolean expectMacros;
List<IDocument> documents;
private DocSourceLocationVisitor nodeVisitor;
/**
* @param expectFullSourceInfo
* @param refText
*/
public NodeVisitor(boolean expectMacros) {
this.expectMacros = expectMacros;
this.documents = new ArrayList<IDocument>();
this.regions = new ArrayList<ISourceRegion>();
this.nodeVisitor = new DocSourceLocationVisitor(this);
}
/* (non-Javadoc)
* @see com.nokia.carbide.internal.api.cpp.epoc.engine.dom.IASTVisitor#visit(com.nokia.carbide.internal.api.cpp.epoc.engine.dom.IASTNode)
*/
public int visit(IASTNode node) {
if (node.getSourceRegion() == null) {
builder.append("null source location in " + node +"\n");
return VISIT_CHILDREN;
}
this.currentNode = node;
nodeVisitor.node = node;
node.getSourceRegion().accept(nodeVisitor);
return VISIT_CHILDREN;
}
}
protected ParserConfigurationBase parserConfig;
protected IPath projectPath;
protected boolean ignoreWhiteSpaceInRefTest;
protected boolean allowParseErrors = false;
private HashMap<String, String> refMap;
protected IPath modelPath;
protected String getTokenAbsolutePath() {
return HostOS.IS_WIN32
? "c:\\tests\\" : "/tmp/";
}
protected String toDosPath(IPath path) {
return path.toPortableString().replaceAll("/", "\\\\");
}
protected String toDosPath(String path) {
return path.replaceAll("/", "\\\\");
}
/* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
@Override
protected void setUp() throws Exception {
super.setUp();
if (projectPath == null)
projectPath = new Path(getTokenAbsolutePath()).append("project");
parserConfig = new ParserConfigurationBase(projectPath) {
@Override
protected IASTTranslationUnit parse(IPath path, IDocument document) {
return BaseTest.this.parse(path, document);
}
};
refMap = new HashMap<String, String>();
if (modelPath == null)
modelPath = projectPath.append("model.txt");
}
protected IASTTranslationUnit parse(IPath path, IDocument document) {
IDocumentParser parser = ParserFactory.createPreParser();
IASTTranslationUnit tu = parser.parse(path, document);
if (!allowParseErrors && parser.hadErrors()) {
ProblemVisitor visitor = new ProblemVisitor();
tu.accept(visitor);
StringBuffer buffer = new StringBuffer();
for (IASTProblemNode node : visitor.getProblems()) {
buffer.append(node.getMessage());
buffer.append('\n');
}
fail(buffer.toString());
}
ASTUtils.validateSourceLocations(tu);
return tu;
}
protected IASTTranslationUnit parse(IPath path, String text) {
return parse(path, DocumentFactory.createDocument(text));
}
protected void validateNewNode(IASTNode node) {
assertFalse(node.isDirty());
assertFalse(node.isDirtyTree());
}
protected void testCopiedTree(IASTNode a, IASTNode b) {
assertFalse(a == b);
assertEquals(a, b);
checkHashCodes(a, b);
assertEquals(a.getNewText(), b.getNewText());
}
/**
* Compare hash nodes for equal trees, which must match at every level.
* @param a
* @param b
*/
protected void checkHashCodes(IASTNode a, IASTNode b) {
IASTNode copy = a.copy();
// recursively copy source info so making sure automatically generated equals/hashcode
// compares fine, since they all compare children down to source info.
//copy.copySourceInfo(b);
recursiveCopySourceInfo(copy, b);
if (!copy.equals(b))
fail("problem with #equals() in parents of " + a.getClass());
IASTNode[] aKids = a.getChildren();
IASTNode[] bKids = b.getChildren();
for (int i = 0; i < aKids.length; i++) {
checkHashCodes(aKids[i], bKids[i]);
}
int aCode = copy.hashCode();
int bCode = b.hashCode();
if (aCode == bCode)
return;
// ensure this isn't due to source changes
recursiveResetSource(copy);
IASTNode bCopy = b.copy();
recursiveResetSource(bCopy);
if (copy.hashCode() == bCopy.hashCode())
return;
// all kids matched, so node itself is bad
fail("problem with #hashCode for " + a.getClass());
}
private void recursiveResetSource(IASTNode node) {
node.copySourceInfo(null);
for (IASTNode kid : node.getChildren())
recursiveResetSource(kid);
}
private void recursiveCopySourceInfo(IASTNode dst, IASTNode src) {
dst.copySourceInfo(src);
IASTNode[] srcKids = src.getChildren();
IASTNode[] dstKids = dst.getChildren();
for (int i = 0; i < srcKids.length; i++) {
recursiveCopySourceInfo(dstKids[i], srcKids[i]);
}
}
/**
* Test that a parsed TU matches a reference TU.
* @param refTu
* @param tu
* @boolean expectPP if true, preprocessing is enabled and source contents may not match, so don't complain;
* else, every node's new text is compared against its source info, reduced to a canonical
* form, and matched
*/
protected void testParsedTu(IASTTranslationUnit refTu, IASTTranslationUnit tu, boolean expectPP) {
checkParentage(null, tu);
assertFalse(tu.isDirtyTree());
if (!refTu.equalValue(tu)) {
assertEquals(refTu.getNewText(), tu.getNewText()); // ensure a diff dialog
// maybe not textually different
fail("TU's return nonequal but are textually equal");
}
// the hash codes depend on the ids on the tree, so set them
int id = 0;
for (IASTTopLevelNode node : refTu.getNodes()) {
node.setId(id++);
}
checkHashCodes(refTu, tu);
assertEquals(refTu.getNewText(), tu.getNewText());
testSourceRegions(tu, expectPP);
assertEquals(tu.copy(), tu);
}
/**
* @param tu
*/
private void checkParentage(IASTNode parent, IASTNode node) {
if (parent != null)
assertTrue(parent == node.getParent());
IASTNode[] kids = node.getChildren();
for (IASTNode kid : kids)
checkParentage(node, kid);
}
/**
* Test that a parsed TU matches a reference TU where it counts.
* @param refTu
* @param tu
*/
protected void testParsedTu(IASTTranslationUnit refTu, IASTTranslationUnit tu) {
testParsedTu(refTu, tu, false);
}
/**
* @param expectMacros
* @param tu
*/
protected void testSourceRegions(IASTNode node, boolean expectMacros) {
NodeVisitor visitor = new NodeVisitor(expectMacros);
node.accept(visitor);
if (visitor.builder.length() != 0)
fail("source region problems:\n" + visitor.builder);
}
/**
* @param expectMacros
* @param tu
*/
protected void testSourceRegions(IView view, boolean expectMacros) {
testSourceRegions(((ViewASTBase)view).getFilteredTranslationUnit(), expectMacros);
}
private static final Pattern commentPattern = Pattern.compile("(?:/\\*(?:[^*]|(?:\\*+[^*/]))*\\*+/)|(?://.*)"); //$NON-NLS-1$
/**
* This strips comments from the text
*/
public static CharSequence stripComments(CharSequence text) {
// remove all the comments
if (text == null)
return null;
Matcher commentMatcher = commentPattern.matcher(text);
text = commentMatcher.replaceAll(""); //$NON-NLS-1$
return text;
}
/**
* Compare two strings canonically.
* @param newText
* @param content
* @return
*/
private boolean compareSource(String ref, String text) {
ref = ref.replaceAll("\\\\(\r\n|\r|\n)", "");
text = text.replaceAll("\\\\(\r\n|\r|\n)", "");
ref = stripComments(ref.toLowerCase()).toString();
text = stripComments(text.toLowerCase()).toString();
ref = ref.replaceAll("(\\s|\r\n|\r|\n)+", "");
text = text.replaceAll("(\\s|\r\n|\r|\n)+", "");
if (!ref.equals(text)) {
return false;
}
return true;
}
protected IASTPreprocessorTokenStream createSimpleTokenStream(String text) {
return ASTFactory.createPreprocessorTokenStream(text);
}
protected void commitTest(IOwnedModel model, IView view, String refText) {
view.commit();
String currentText = model.getDocument().get();
assertTextEquals(refText, currentText, ignoreWhiteSpaceInRefTest);
// do it again to ensure nothing breaks: second commit can't rewrite doc
// or fire a change
final boolean changed[ ] = {false, false};
IDocumentListener listener = new IDocumentListener() {
public void documentAboutToBeChanged(DocumentEvent event) {
changed[0] = true;
}
public void documentChanged(DocumentEvent event) {
changed[1] = true;
}
};
model.getDocument().addDocumentListener(listener);
view.commit();
String secondRoundText = model.getDocument().get();
assertEquals("Unexpected change in second commit!", currentText, secondRoundText);
assertFalse("Unexpected change in second commit!", changed[0] && changed[1]);
model.getDocument().removeDocumentListener(listener);
}
/**
* Compare text, optionally ignoring differences in whitespace (but not utter deletion)
* @param refText
* @param string
* @param ignoreWhiteSpace
*/
protected void assertTextEquals(String refText, String docText, boolean ignoreWhiteSpace) {
if (ignoreWhiteSpace) {
refText = refText.replaceAll("[ \t\f]+", " ");
refText = TextUtils.canonicalizeNewlines(refText);
docText = docText.replaceAll("[ \t\f]+", " ");
docText = TextUtils.canonicalizeNewlines(docText);
}
assertEquals(refText, docText);
}
protected void match(String[] entries, List list) {
StringBuilder builder = new StringBuilder();
int idx = 0;
for (Object obj : list) {
if (idx >= entries.length) {
builder.append("extra entry " + obj+"\n");
} else if (!obj.toString().equals(entries[idx])) {
builder.append("mismatch, expected " + entries[idx] +", got " + obj+"\n");
}
idx++;
}
while (idx < entries.length) {
builder.append("missing entry " + entries[idx++]+"\n");
}
if (builder.length() > 0)
fail(builder.toString());
}
protected void match(IPath[] entries, IPath[] paths) {
StringBuilder builder = new StringBuilder();
int idx = 0;
for (Object obj : paths) {
if (idx >= entries.length) {
builder.append("extra entry " + obj+"\n");
} else if (!obj.equals(entries[idx])) {
builder.append("mismatch, expected " + entries[idx] +", got " + obj+"\n");
}
idx++;
}
while (idx < entries.length) {
builder.append("missing entry " + entries[idx++]+"\n");
}
if (builder.length() > 0)
fail(builder.toString());
}
/**
* Waste time so Eclipse will run resource listeners.
*
*/
protected void sleep() throws Exception {
Job job = new WorkspaceJob("foo") {
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
monitor.beginTask("foo", 2);
monitor.worked(1);
monitor.worked(1);
monitor.done();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
return Status.OK_STATUS;
}
};
job.schedule();
job.join();
}
/**
* Load a file relative to this plugin in the running workbench
* @param file
* @return File
* @throws IOException
*/
public static File pluginRelativeFile(String file) throws IOException {
if (TestsPlugin.getDefault() == null)
return null;
Bundle bundle = TestsPlugin.getDefault().getBundle();
if (bundle == null)
return null;
URL url = FileLocator.find(bundle, new Path("."), null);
if (url == null)
TestCase.fail("could not make URL from bundle " + bundle + " and path " + file);
url = FileLocator.resolve(url);
TestCase.assertEquals("file", url.getProtocol());
return new File(url.getPath(), file);
}
/**
* Find a file relative to the project. Works if running
* in the workbench or standalone.
* @param file the relative path (from the project) to the file
* @return File
*/
public static File projectRelativeFile(String file) throws Exception {
File f;
if (!Platform.isRunning()) {
// get file relative to CWD (i.e. this project)
f = new File(file);
f = f.getCanonicalFile();
} else {
// get file relative to running plugin (still this project)
f = pluginRelativeFile(file);
}
if (f == null)
TestCase.fail("Cannot find file " + file + " relative to project");
return f;
}
protected IASTProblemNode[] getProblems(IASTNode node) {
ProblemVisitor visitor = new ProblemVisitor();
node.accept(visitor);
return visitor.getProblems();
}
protected IASTProblemNode[] getProblems(IView view) {
return getProblems(((ViewASTBase)view).getFilteredTranslationUnit());
}
protected void checkBlockInfo(IConditionalBlock rootBlock) {
StringBuilder builder = new StringBuilder();
checkBlockInfo(builder, rootBlock);
if (builder.length() > 0)
fail(builder.toString());
}
/**
* @param rootBlock
*/
private void checkBlockInfo(StringBuilder builder, IConditionalBlock block) {
ISourceRegion span = block.getSpan();
if (span == null) {
builder.append("Failed to get span for block: " + block);
return;
}
ISourceRegion fullSpan = block.getFullSpan();
if (fullSpan == null) {
builder.append("Failed to get full span for block: " + block);
return;
}
if (!fullSpan.contains(span)) {
builder.append("Full span does not include span: " + block);
return;
}
IConditionalBlock[] kids = block.getChildren();
for (IConditionalBlock kid : kids) {
checkBlockInfo(builder, kid);
ISourceRegion kidSpan = kid.getSpan();
if (kidSpan != null) {
if (!kidSpan.contains(span) && !span.contains(kidSpan)) {
builder.append("child span extends beyond parent: " + kid + " ==> " + block);
}
}
kidSpan = kid.getFullSpan();
if (kidSpan != null) {
if (!kidSpan.contains(fullSpan) && !fullSpan.contains(kidSpan)) {
builder.append("child full span extends beyond parent: " + kid + " ==> " + block);
}
}
}
}
/**
* Check that problems look sane
* @param messages
*/
protected void checkMessages(IMessage[] messages) {
for (IMessage message : messages) {
assertNotNull(message.getMessage());
assertFalse(message.getMessage().matches("(?s)!.*!"));
}
}
/**
* Check that problems look sane
* @param messages
*/
protected void checkMessages(Collection<IMessage> messages) {
for (IMessage message : messages) {
assertNotNull(message.getMessage());
assertFalse(message.getMessage().matches("(?s)!.*!"));
}
}
/**
* Check that problems look sane
* @param messages
*/
protected void checkMessages(IASTProblemNode[] problems) {
for (IASTProblemNode problem : problems) {
assertNotNull(problem.getMessage());
assertFalse(problem.getMessage().getMessage().matches("!.*!"));
}
}
protected void assertNoProblems(IASTNode node) {
IASTProblemNode[] problems = getProblems(node);
if (problems.length > 0) {
StringBuilder builder = new StringBuilder();
for (IASTProblemNode problem : problems) {
builder.append(problem.getMessage());
builder.append('\n');
}
fail("problems detected:\n"+builder.toString());
}
}
protected void assertNoMessages(IMessage[] messages) {
if (messages.length > 0) {
StringBuilder builder = new StringBuilder();
for (IMessage message : messages) {
builder.append(message);
builder.append('\n');
}
fail("problems detected:\n"+builder.toString());
}
}
protected void checkNoProblems(IView view) {
if (view instanceof ViewASTBase)
assertNoProblems(((ViewASTBase)view).getFilteredTranslationUnit());
else
assertNoMessages(view.getMessages());
}
protected String loadFileText(String filepath) throws Exception {
File file = projectRelativeFile(filepath);
return new String(FileUtils.readFileContents(file, null));
}
protected void dumpSourceInfo(IASTNode node) {
node.accept(new IASTVisitor() {
public int visit(IASTNode node) {
System.out.println(node.getSourceRegion() + ": " + node);
return IASTVisitor.VISIT_CHILDREN;
}
});
}
/**
* Look for a file in the filesystem given a full path that
* would appear in an ISourceLocation
* @param fs
* @param location
* @return filename used in fs
*/
protected String lookupRefFile(Map<String, String> fs, IPath location) {
for (Map.Entry<String, String> entry : fs.entrySet()) {
if (entry.getKey() != null) {
if (entry.getKey().equalsIgnoreCase(location.lastSegment())
|| entry.getKey().equalsIgnoreCase(location.toOSString())) {
return entry.getKey();
}
}
}
return null;
}
protected void runRefTest_(IView view, ISourceRegion mainRegion, final Map<String, String> refMap) {
// update
view.commit();
// look for locations in the TU's main location, which should
// span any contained locations, and ensure all the files were
// updated as expected
final Set<String> visitedFiles = new HashSet<String>();
mainRegion.accept(new SourceLocationVisitorAdapter() {
public int visit(IDocumentSourceRegion docRegion) {
String file = lookupRefFile(refMap, docRegion.getLocation());
if (file == null)
fail("reference to unknown file: " + docRegion.getLocation());
assertEquals(refMap.get(file),
docRegion.getDocument().get());
// test that we only see it once
visitedFiles.add(file);
return VISIT_CHILDREN;
}
});
// ensure we saw all the expected files
// assertEquals(refMap.size(), visitedFiles.size());
}
/**
* Run the rewriting test on the given tu, which may span multiple
* documents. The refMap contains the mapping of filenames
* (registered in parserConfig.fs) to the new contents. Any files
* no longer expected to be referenced should be removed.
* <p>
* We run the test twice, once on
* the original tu and once on a copy, ensuring the results are the
* same, as a double-check of the copying of the dirty state.
* @param tu
* @param refText
*/
protected void runRefTest(IView view) {
IASTTranslationUnit tu = null;
tu = ((ViewASTBase) view).getFilteredTranslationUnit();
// save off the documents before the test
final Map<IPath, IDocument> origDocs = new HashMap<IPath, IDocument>();
tu.getSourceRegion().accept(new SourceLocationVisitorAdapter() {
public int visit(IDocumentSourceRegion region) {
IDocumentSourceRegion docRegion = (IDocumentSourceRegion) region;
if (!origDocs.containsKey(docRegion.getLocation())) {
origDocs.put(docRegion.getLocation(), DocumentFactory.createDocument(docRegion.getDocument().get()));
}
return VISIT_CHILDREN;
}
});
runRefTest_(view, tu.getSourceRegion(), refMap);
/*
// restore docs for another go
tu.getSourceRegion().accept(new SourceLocationVisitorAdapter() {
public int visit(IDocumentSourceRegion docRegion) {
if (origDocs.containsKey(docRegion.getLocation())) {
docRegion.getDocument().set(origDocs.get(docRegion.getLocation()).get());
origDocs.remove(docRegion.getLocation());
}
return VISIT_CHILDREN;
}
});
view.forceSynchronized();
runRefTest_(view, tu.getSourceRegion(), refMap);
*/
}
protected Map<String, String> copyFilesystem() {
return new HashMap<String, String>(parserConfig.getFilesystem());
}
protected void addRefFile(String filename, String text) {
addRefFile(filename, text, null);
}
protected void addRefFile(String filename, String text, String refText) {
if (filename == null)
filename = modelPath.lastSegment();
else
filename = projectPath.append(filename).toOSString();
parserConfig.getFilesystem().put(filename, text);
refMap.put(filename, refText != null ? refText : text);
}
protected ASTPreprocessor createPreprocessor(IViewParserConfiguration parserConfiguration) {
return new ASTPreprocessor(parserConfiguration.getTranslationUnitProvider(),
parserConfiguration.getIncludeFileLocator(), parserConfiguration.getModelDocumentProvider());
}
public static String cleanNewlines(String text) {
Pattern p = Pattern.compile("\r\n|\r(?!\n)", Pattern.MULTILINE);
Matcher m = p.matcher(text);
return m.replaceAll("\n");
}
/**
* Read the contents of the file into a string, optionally cleaning
* it up for ease of comparison
* @param file full path or project-relative file
* @param whitespaceCleaning if true, remove leading/trailing space from the
* file text
* @param newlineCleaning if true, convert newlines from file text to canonical format ('\n')
*/
public static String readFile(File file, boolean whitespaceCleaning, boolean newlineCleaning) throws IOException {
StringBuffer sb = new StringBuffer();
try {
Reader reader = new InputStreamReader(
new FileInputStream(file), "UTF-8");
char[] buf = new char[1024];
int len;
while ((len = reader.read(buf)) > 0) {
sb.append(buf, 0, len);
}
reader.close();
} catch (IOException e) {
TestCase.fail("Couldn't find " + file);
}
if (whitespaceCleaning) {
int idx = 0;
while (idx < sb.length() && Character.isWhitespace(sb.charAt(idx)))
idx++;
sb.delete(0, idx);
idx = sb.length();
while (idx > 0 && Character.isWhitespace(sb.charAt(idx - 1)))
idx--;
sb.delete(idx, sb.length());
}
String sbText = sb.toString();
if (newlineCleaning) {
sbText = cleanNewlines(sbText);
}
return sbText;
}
/**
* Read the contents of the file into a string, optionally cleaning
* it up for ease of comparison
* @param file full path or project-relative file
* @param whitespaceCleaning if true, remove leading/trailing space from the
* file text
* @param newlineCleaning if true, convert newlines from file text to canonical format ('\n')
*/
public static String readFile(String file, boolean whitespaceCleaning, boolean newlineCleaning) throws Exception {
File f;
f = new File(file);
if (!f.exists())
f = projectRelativeFile(file);
return readFile(f, whitespaceCleaning, newlineCleaning);
}
/**
* Compare the contents of the file to the text string, optionally
* cleaning them up to make comparisons easier
* @param file project-relative file
* @param txt
* @param whitespaceCleaning if true, remove <b>leading and trailing</b>
* space from the file text and the canonical text
* @param newlineCleaning if true, convert newlines from file text to canonical format ('\n')
*/
public static void compareFiles(String file, String txt, boolean whitespaceCleaning, boolean newlineCleaning) throws Exception {
String fileText = readFile(file, whitespaceCleaning, newlineCleaning);
if (whitespaceCleaning) {
int idx = 0;
while (idx < txt.length() && Character.isWhitespace(txt.charAt(idx)))
idx++;
txt = txt.substring(idx);
idx = txt.length();
while (idx > 0 && Character.isWhitespace(txt.charAt(idx - 1)))
idx--;
txt = txt.substring(0, idx);
}
TestCase.assertEquals(fileText, txt);
}
/**
* Compare two trees of files recursively.<p>
* Any file in refDir must exist in projDir.<p>
* Any file in projDir, except for dot-files, must exist in refDir.<p>
* And the contents must match, excepting leading and trailing whitespace.
* @param refDir
* @param projDir
*/
public static void compareTrees(File refDir, File projDir) throws Exception {
if (!refDir.exists() || !refDir.isDirectory())
TestCase.fail("no such reference directory: " + refDir);
if (!projDir.exists() || !projDir.isDirectory())
TestCase.fail("no such project directory: " + projDir);
File[] refs = refDir.listFiles();
for (int i = 0; i < refs.length; i++) {
File projRef = new File(projDir, refs[i].getName());
if (refs[i].isDirectory()) {
if (refs[i].getName().matches("CVS"))
continue;
compareTrees(refs[i], projRef);
}
else {
if (!projRef.exists())
TestCase.fail("missing file in project: " + projRef);
else
compareFiles(refs[i], projRef, true, true);
}
}
File[] projs = projDir.listFiles();
for (int i = 0; i < projs.length; i++) {
// ignore project dotfiles and any backups made
if (!projs[i].getName().startsWith(".") && !projs[i].getName().toLowerCase().endsWith(".bak")) {
File refRef = new File(refDir, projs[i].getName());
if (!refRef.exists()) {
TestCase.fail("unexpected additional file in project: " + projs[i]);
}
}
}
}
/**
* Compare the contents of two files, optionally
* cleaning them up to make comparisons easier
* @param refFile reference file
* @param otherFile file to compare with
* @param whitespaceCleaning if true, remove <b>leading and trailing</b>
* space from the file text and the canonical text
* @param newlineCleaning if true, convert newlines from file text to canonical format ('\n')
*/
public static void compareFiles(File refFile, File otherFile,
boolean whitespaceCleaning, boolean newlineCleaning)
throws Exception {
String refText = readFile(refFile, whitespaceCleaning, newlineCleaning);
String otherText = readFile(otherFile, whitespaceCleaning,
newlineCleaning);
TestCase.assertEquals(refText, otherText);
}
public static void compareFiles(String message, File refFile,
File otherFile, boolean whitespaceCleaning, boolean newlineCleaning)
throws Exception {
String refText = readFile(refFile, whitespaceCleaning, newlineCleaning);
String otherText = readFile(otherFile, whitespaceCleaning,
newlineCleaning);
TestCase.assertEquals(message, refText, otherText);
}
}