blob: 1d44c7f0fd8143a815ce5b3036116b7c6ba89971 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 Nokia Corporation.
* 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:
* Ed Swartz (Nokia) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.autotools.tests.autoconf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.cdt.autotools.ui.editors.parser.AutoconfElement;
import org.eclipse.cdt.autotools.ui.editors.parser.AutoconfMacroDetector;
import org.eclipse.cdt.autotools.ui.editors.parser.AutoconfMacroElement;
import org.eclipse.cdt.autotools.ui.editors.parser.AutoconfParser;
import org.eclipse.cdt.autotools.ui.editors.parser.IAutoconfErrorHandler;
import org.eclipse.cdt.autotools.ui.editors.parser.IAutoconfMacroValidator;
import org.eclipse.cdt.autotools.ui.editors.parser.ParseException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.junit.Before;
public abstract class BaseParserTest {
private IAutoconfErrorHandler errorHandler;
protected List<ParseException> errors;
private IAutoconfMacroValidator macroValidator;
private Set<String> macroNames;
private AutoconfMacroDetector macroDetector;
@Before
public void setUp() {
errors = new ArrayList<>();
this.errorHandler = (ParseException exception) -> {
assertNotNull(exception);
errors.add(exception);
};
this.macroDetector = new AutoconfMacroDetector();
macroNames = new HashSet<>();
this.macroValidator = (AutoconfMacroElement element) -> {
assertNotNull(element);
assertNotNull(element.getName());
assertNotNull(element.getChildren());
macroNames.add(element.getName());
};
}
protected IDocument createDocument(String text) {
return new Document(text);
}
/**
* Parse the document with no error or macro handlers
* @param parser
* @param document
* @return root
*/
protected AutoconfElement parseNoHandlers(IDocument document) {
AutoconfParser parser = new AutoconfParser(null, null, null);
AutoconfElement root = parser.parse(document);
assertNotNull(root);
return root;
}
/**
* Parse the document with the standard error or macro handlers,
* cleared out before use
* @param parser
* @param document
* @return root
*/
protected AutoconfElement parseWithHandlers(IDocument document) {
AutoconfParser parser = new AutoconfParser(errorHandler, macroDetector, macroValidator);
errors.clear();
macroNames.clear();
AutoconfElement root = parser.parse(document);
assertNotNull(root);
return root;
}
/**
* Parse the document in 'string' twice, once without any handlers and once with the standard error or macro handlers,
* cleared out before use
* @param string
* @return root
*/
protected AutoconfElement parse(String string) {
AutoconfElement tree = parse(string, false);
return tree;
}
protected AutoconfElement parse(String string, boolean allowErrors) {
IDocument document = createDocument(string);
AutoconfElement root1 = parseNoHandlers(document);
assertNotNull(root1);
AutoconfElement root2 = parseWithHandlers(document);
assertNotNull(root2);
validateSourceTree(root1);
validateSourceTree(root2);
// TODO: check root1 == root2
// ensure the doc wasn't changed (sanity)
assertEquals(string, document.get());
if (!allowErrors) {
if (!errors.isEmpty())
fail("got errors" + errors.get(0));
}
else
assertFalse(errors.isEmpty());
return root2;
}
protected void checkError(String msgKey) {
for (ParseException exc: errors) {
if (exc.getMessage().contains(msgKey))
return;
}
String any = "";
if (!errors.isEmpty())
any = ", but saw " + errors.get(0).toString();
fail("did not find error: " + msgKey + any );
}
protected void checkError(String msgKey, int line) {
ParseException possible = null;
int distance = 999;
for (ParseException exc : errors) {
if (exc.getMessage().contains(msgKey)) {
int curDistance = Math.abs(exc.getLineNumber() - line);
if (curDistance < distance) {
possible = exc;
distance = curDistance;
}
if (exc.getLineNumber() == line)
return;
}
}
if (possible == null)
fail("did not find any error: " + msgKey);
else
fail("did not find error: " + msgKey + " on line: " + line +", but found one at " + possible.getLineNumber());
}
/**
* Make sure the source ranges for the tree are sane:
* <p>
* <li>document set
* <li>line numbers, columns valid
* <li>children encompassed in parent
* <li>siblings non-overlapping
* @param element
*/
protected void validateSourceTree(AutoconfElement element) {
validateSourceElement(element);
AutoconfElement[] kids = element.getChildren();
for (int i = 0; i < kids.length; i++) {
if (kids[i].getStartOffset() < element.getStartOffset()
|| kids[i].getEndOffset() > element.getEndOffset())
fail(describeElement(kids[i]) + " not inside parent " + describeElement(element));
validateSourceTree(kids[i]);
}
for (int i = 0; i < kids.length - 1; i++) {
AutoconfElement kid1 = kids[i];
AutoconfElement kid2 = kids[i + 1];
if (kid1.getEndOffset() > kid2.getStartOffset())
fail(describeElement(kid1) + " overlaps " + describeElement(kid2));
}
}
/**
* Make sure the source ranges for the element are sane:
* <p>
* <li>document set
* <li>line numbers, columns valid
* @param element
*/
private void validateSourceElement(AutoconfElement element) {
if (element.getDocument() == null)
fail("no document for " + describeElement(element));
if (element.getStartOffset() < 0)
fail("no start offset for " + describeElement(element));
if (element.getEndOffset() < 0)
fail("no end offset for " + describeElement(element));
if (element.getStartOffset() > element.getEndOffset())
fail("invalid range (start > end) for " + describeElement(element));
}
private String describeElement(AutoconfElement element) {
return element.getClass().getSimpleName() + " <<" + element.getSource() + ">>";
}
protected void assertEqualSource(String text, AutoconfElement element) {
assertEquals(text, element.getSource());
}
/** Check that a tree has the given structure. 'elements' is a flattened
* representation of the tree, where each node represents one level deeper
* in the tree, except for 'null' which backs out one level.
* @param tree
* @param elements
*/
protected void assertTreeStructure(AutoconfElement tree, String[] elements) {
// if fails, the elements[] array is missing final nulls
assertEquals(elements.length, assertTreeStructure(tree, elements, 0));
}
private int assertTreeStructure(AutoconfElement tree, String[] elements,
int elementIdx) {
AutoconfElement[] kids = tree.getChildren();
for (int j = 0; j < kids.length; j++) {
if (elementIdx >= elements.length || elements[elementIdx] == null) {
fail("extra children in " + tree + " at " + kids[j]);
}
if (!kids[j].getName().equals(elements[elementIdx]))
fail("did not match " + elements[elementIdx] + ", instead got " + kids[j].getClass().getSimpleName() + "=" + kids[j].getName());
elementIdx++;
if (kids[j].getChildren().length > 0) {
elementIdx = assertTreeStructure(kids[j], elements, elementIdx);
if (elementIdx >= elements.length)
fail("Missing null in elements list, or invalid tree hierarchy");
if (elements[elementIdx] != null) {
fail("not enough children in " + tree);
}
elementIdx++;
}
}
return elementIdx;
}
}