Skip to content

Commit

Permalink
Support Context tags in Boost test runs
Browse files Browse the repository at this point in the history
Boost a while ago added Context tags, this commit makes
those context tags supported in the testsrunner code of CDT

See also https://www.boost.org/doc/libs/1_82_0/libs/test/doc/html/boost_test/test_output/test_tools_support_for_logging/contexts.html

Fixes 459
  • Loading branch information
jonahgraham committed Aug 14, 2023
1 parent 7caa0c0 commit f59c109
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011, 2012 Anton Gorenkov.
* Copyright (c) 2011, 2023 Anton Gorenkov and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -21,6 +21,10 @@ public class BoostTestsRunnerMessages extends NLS {
public static String BoostTestsRunner_io_error_prefix;
public static String BoostTestsRunner_wrong_tests_paths_count;
public static String BoostTestsRunner_xml_error_prefix;
public static String BoostXmlLogHandler_ContextHeader;
public static String BoostXmlLogHandler_ContextOverflow;
public static String BoostXmlLogHandler_ContextPrefix;
public static String BoostXmlLogHandler_ContextSuffix;
public static String BoostXmlLogHandler_exception_suffix;
public static String BoostXmlLogHandler_wrong_tag_name;
static {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
###############################################################################
# Copyright (c) 2011 Anton Gorenkov
# Copyright (c) 2011, 2023 Anton Gorenkov and others.
#
# This program and the accompanying materials
# are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -15,5 +15,9 @@ BoostTestsRunner_error_format={0}: {1}
BoostTestsRunner_io_error_prefix=I/O Error
BoostTestsRunner_wrong_tests_paths_count=Only on test suite or test case should be specified to rerun
BoostTestsRunner_xml_error_prefix=XML parse error
BoostXmlLogHandler_ContextHeader=\nContext:\u0020
BoostXmlLogHandler_ContextOverflow=...
BoostXmlLogHandler_ContextPrefix=\u0020[
BoostXmlLogHandler_ContextSuffix=]
BoostXmlLogHandler_exception_suffix=\nLast check point was here.
BoostXmlLogHandler_wrong_tag_name=Invalid XML format: Element "{0}" is not accepted\!
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011, 2013 Anton Gorenkov and others.
* Copyright (c) 2011, 2023 Anton Gorenkov and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -46,13 +46,22 @@ public class BoostXmlLogHandler extends DefaultHandler {
private static final String XML_NODE_ERROR = "Error"; //$NON-NLS-1$
private static final String XML_NODE_FATAL_ERROR = "FatalError"; //$NON-NLS-1$
private static final String XML_NODE_EXCEPTION = "Exception"; //$NON-NLS-1$
private static final String XML_NODE_CONTEXT = "Context"; //$NON-NLS-1$
private static final String XML_NODE_FRAME = "Frame"; //$NON-NLS-1$

// Boost.Test XML log attributes
private static final String XML_ATTR_TEST_SUITE_NAME = "name"; //$NON-NLS-1$
private static final String XML_ATTR_TEST_CASE_NAME = "name"; //$NON-NLS-1$
private static final String XML_ATTR_MESSAGE_FILE = "file"; //$NON-NLS-1$
private static final String XML_ATTR_MESSAGE_LINE = "line"; //$NON-NLS-1$

/**
* The context can be of arbitrary length, to prevent excessively long strings
* in the tree limit the context to this length in the tree. The full context
* is available in the details tab.
*/
private static final int MAX_CONTEXT_LENGTH_IN_TREE = 50;

/** The default file name for test message location. */
private static final String DEFAULT_LOCATION_FILE = null;

Expand Down Expand Up @@ -81,6 +90,10 @@ public class BoostXmlLogHandler extends DefaultHandler {
private String lastTestCaseName = ""; //$NON-NLS-1$
private static final int SAME_TEST_CASE_NAME_COUNT_START = 2;
private int sameTestCaseNameCount = SAME_TEST_CASE_NAME_COUNT_START;
private StringBuilder context = new StringBuilder();

private boolean testCaseEnterDeferred = false;
private StringBuilder testCaseName = new StringBuilder();

BoostXmlLogHandler(ITestModelUpdater modelUpdater) {
this.modelUpdater = modelUpdater;
Expand All @@ -102,17 +115,18 @@ public void startElement(String namespaceURI, String localName, String qName, At
break;

case XML_NODE_TEST_CASE:
String testCaseName = attrs.getValue(XML_ATTR_TEST_CASE_NAME);
testCaseName.setLength(0);
testCaseName.append(attrs.getValue(XML_ATTR_TEST_CASE_NAME));

if (lastTestCaseName.equals(testCaseName)) {
testCaseName += " (" + sameTestCaseNameCount + ")"; //$NON-NLS-1$ //$NON-NLS-2$
if (lastTestCaseName.equals(testCaseName.toString())) {
testCaseName.append(" (" + sameTestCaseNameCount + ")"); //$NON-NLS-1$ //$NON-NLS-2$
++sameTestCaseNameCount;
} else {
lastTestCaseName = testCaseName;
lastTestCaseName = testCaseName.toString();
sameTestCaseNameCount = SAME_TEST_CASE_NAME_COUNT_START;
}

modelUpdater.enterTestCase(testCaseName);
testCaseEnterDeferred = true;
testStatus = Status.Passed;
break;

Expand All @@ -132,6 +146,11 @@ public void startElement(String namespaceURI, String localName, String qName, At
lineNumber = DEFAULT_LOCATION_LINE;
break;

case XML_NODE_CONTEXT:
case XML_NODE_FRAME:
/* handle in endElement */
break;

case XML_NODE_TESTING_TIME:
case XML_NODE_TEST_LOG:
/* just skip, do nothing */
Expand All @@ -149,7 +168,26 @@ public void startElement(String namespaceURI, String localName, String qName, At
* @param level test message level
*/
private void addCurrentMessage(ITestMessage.Level level) {
modelUpdater.addTestMessage(fileName, lineNumber, level, elementDataStack.peek().toString().trim());
String text = elementDataStack.peek().toString().trim();
if (testCaseEnterDeferred) {
if (!context.isEmpty()) {
testCaseName.append(BoostTestsRunnerMessages.BoostXmlLogHandler_ContextPrefix);
if (context.length() > MAX_CONTEXT_LENGTH_IN_TREE) {
testCaseName.append(context.subSequence(0, MAX_CONTEXT_LENGTH_IN_TREE));
testCaseName.append(BoostTestsRunnerMessages.BoostXmlLogHandler_ContextOverflow);
} else {
testCaseName.append(context);
}
testCaseName.append(BoostTestsRunnerMessages.BoostXmlLogHandler_ContextSuffix);
}
modelUpdater.enterTestCase(testCaseName.toString());
testCaseEnterDeferred = false;
}
if (!context.isEmpty()) {
text += BoostTestsRunnerMessages.BoostXmlLogHandler_ContextHeader + context.toString().trim();
context.setLength(0);
}
modelUpdater.addTestMessage(fileName, lineNumber, level, text.trim());
fileName = DEFAULT_LOCATION_FILE;
lineNumber = DEFAULT_LOCATION_LINE;
if (level == ITestMessage.Level.Error || level == ITestMessage.Level.FatalError) {
Expand All @@ -174,6 +212,10 @@ public void endElement(String namespaceURI, String localName, String qName) thro
break;

case XML_NODE_TEST_CASE:
if (testCaseEnterDeferred) {
modelUpdater.enterTestCase(testCaseName.toString());
testCaseEnterDeferred = false;
}
modelUpdater.setTestStatus(testStatus);
modelUpdater.exitTestCase();
break;
Expand Down Expand Up @@ -209,6 +251,13 @@ public void endElement(String namespaceURI, String localName, String qName) thro
addCurrentMessage(ITestMessage.Level.Exception);
break;

case XML_NODE_CONTEXT:
context.insert(0, elementDataStack.peek().toString().trim());
break;
case XML_NODE_FRAME:
context.append(elementDataStack.peek().toString().trim());
break;

case XML_NODE_TEST_LOG:
case XML_NODE_LAST_CHECKPOINT:
/* just skip, do nothing */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011, 2013 Anton Gorenkov and others.
* Copyright (c) 2011, 2023 Anton Gorenkov and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -507,4 +507,136 @@ public void testParameterizedTests() {
mockModelUpdater.exitTestCase();
mockModelUpdater.exitTestSuite();
}

//<TestLog>
// <TestSuite name="MainTS">
// <TestCase name="single-context">
// <Info file="file1.cpp" line="1">
// <Context>
// <Frame>
// <![CDATA[context 1 message here]]>
// </Frame>
// </Context>
// </Info>
// </TestCase>
// <TestCase name="single-context-and-info-message">
// <Info file="file2.cpp" line="2">
// <![CDATA[info 2 message here]]>
// <Context>
// <Frame>
// <![CDATA[context 2 message here]]>
// </Frame>
// </Context>
// </Info>
// </TestCase>
// <TestCase name="multi-context">
// <Info file="file3.cpp" line="1">
// <Context>
// <Frame>
// <![CDATA[context 3a message here]]>
// </Frame>
// </Context>
// </Info>
// <Info file="file3.cpp" line="2">
// <Context>
// <Frame>
// <![CDATA[context 3b message here]]>
// </Frame>
// </Context>
// </Info>
// </TestCase>
// <TestCase name="error-message">
// <Error file="file4.cpp" line="1">
// <![CDATA[error 4 message here]]>
// <Context>
// <Frame>
// <![CDATA[context 4 message here]]>
// </Frame>
// </Context>
// </Error>
// </TestCase>
// <TestCase name="off-by-one-check-a">
// <Error file="off-by-one.cpp" line="1">
// <Context>
// <Frame>
// <![CDATA[very very very long context off-by-one-check 01234567890]]>
// </Frame>
// </Context>
// </Error>
// </TestCase>
// <TestCase name="off-by-one-check-b">
// <Error file="off-by-one.cpp" line="1">
// <Context>
// <Frame>
// <![CDATA[very very very long context off-by-one-check 012345]]>
// </Frame>
// </Context>
// </Error>
// </TestCase>
// <TestCase name="off-by-one-check-c">
// <Error file="off-by-one.cpp" line="1">
// <Context>
// <Frame>
// <![CDATA[very very very long context off-by-one-check 01234]]>
// </Frame>
// </Context>
// </Error>
// </TestCase>
// <TestCase name="off-by-one-check-d">
// <Error file="off-by-one.cpp" line="1">
// <Context>
// <Frame>
// <![CDATA[very very very long context off-by-one-check 0123]]>
// </Frame>
// </Context>
// </Error>
// </TestCase>
// </TestSuite>
//</TestLog>
public void testWithContextTests() {
mockModelUpdater.skipCalls("setTestStatus");

mockModelUpdater.enterTestSuite("MainTS");
mockModelUpdater.enterTestCase("single-context [context 1 message here]");
mockModelUpdater.addTestMessage("file1.cpp", 1, ITestMessage.Level.Info, "Context: context 1 message here");
mockModelUpdater.exitTestCase();
mockModelUpdater.enterTestCase("single-context-and-info-message [context 2 message here]");
mockModelUpdater.addTestMessage("file2.cpp", 2, ITestMessage.Level.Info,
"info 2 message here\nContext: context 2 message here");
mockModelUpdater.exitTestCase();

// I am not convinced the handling of this is correct, there is an assumption in the code that the first
// context seen in an info (or error/warning/etc) message is the one to display next to the test case
// name. This works if all the info messages have the same context, but is that an oversimplification?
// See https://github.com/eclipse-cdt/cdt/issues/459#issuecomment-1677589998
mockModelUpdater.enterTestCase("multi-context [context 3a message here]");
mockModelUpdater.addTestMessage("file3.cpp", 1, ITestMessage.Level.Info, "Context: context 3a message here");
mockModelUpdater.addTestMessage("file3.cpp", 2, ITestMessage.Level.Info, "Context: context 3b message here");
mockModelUpdater.exitTestCase();

mockModelUpdater.enterTestCase("error-message [context 4 message here]");
mockModelUpdater.addTestMessage("file4.cpp", 1, ITestMessage.Level.Error,
"error 4 message here\nContext: context 4 message here");
mockModelUpdater.exitTestCase();

// These tests make sure there is no off-by-one error when shortening context strings
mockModelUpdater.enterTestCase("off-by-one-check-a [very very very long context off-by-one-check 01234...]");
mockModelUpdater.addTestMessage("off-by-one.cpp", 1, ITestMessage.Level.Error,
"Context: very very very long context off-by-one-check 01234567890");
mockModelUpdater.exitTestCase();
mockModelUpdater.enterTestCase("off-by-one-check-b [very very very long context off-by-one-check 01234...]");
mockModelUpdater.addTestMessage("off-by-one.cpp", 1, ITestMessage.Level.Error,
"Context: very very very long context off-by-one-check 012345");
mockModelUpdater.exitTestCase();
mockModelUpdater.enterTestCase("off-by-one-check-c [very very very long context off-by-one-check 01234]");
mockModelUpdater.addTestMessage("off-by-one.cpp", 1, ITestMessage.Level.Error,
"Context: very very very long context off-by-one-check 01234");
mockModelUpdater.exitTestCase();
mockModelUpdater.enterTestCase("off-by-one-check-d [very very very long context off-by-one-check 0123]");
mockModelUpdater.addTestMessage("off-by-one.cpp", 1, ITestMessage.Level.Error,
"Context: very very very long context off-by-one-check 0123");
mockModelUpdater.exitTestCase();

mockModelUpdater.exitTestSuite();
}
}

0 comments on commit f59c109

Please sign in to comment.