Skip to content

Commit b062e20

Browse files
Kimmo Linnavuod471061c
authored andcommitted
Add initial support for qmake
Based heavily on make and Rust plugins No tests yet, but one example project
1 parent abc1818 commit b062e20

File tree

16 files changed

+761
-0
lines changed

16 files changed

+761
-0
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@
233233
<module>tmc-langs-python3</module>
234234
<module>tmc-langs-notests</module>
235235
<!--module>tmc-langs-rust</module-->
236+
<module>tmc-langs-qmake</module>
236237
<!-- TODO: add plugins and support libraries here -->
237238

238239
<module>tmc-langs-util</module>

tmc-langs-qmake/pom.xml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<groupId>fi.helsinki.cs.tmc</groupId>
6+
<artifactId>tmc-langs</artifactId>
7+
<version>0.7.7-SNAPSHOT</version>
8+
</parent>
9+
10+
<properties>
11+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
12+
<maven.compiler.source>1.7</maven.compiler.source>
13+
<maven.compiler.target>1.7</maven.compiler.target>
14+
<tmc.basedir>../target</tmc.basedir>
15+
</properties>
16+
17+
<artifactId>tmc-langs-qmake</artifactId>
18+
<packaging>jar</packaging>
19+
20+
<!-- Deploy to maven.testmycode.net/nexus -->
21+
<distributionManagement>
22+
<repository>
23+
<id>tmc</id>
24+
<name>TMC releases</name>
25+
<url>http://maven.testmycode.net/nexus/content/repositories/releases</url>
26+
</repository>
27+
<snapshotRepository>
28+
<id>tmc-snapshots</id>
29+
<name>TMC snapshots</name>
30+
<url>http://maven.testmycode.net/nexus/content/repositories/snapshots</url>
31+
</snapshotRepository>
32+
</distributionManagement>
33+
34+
<dependencies>
35+
<dependency>
36+
<groupId>fi.helsinki.cs.tmc</groupId>
37+
<artifactId>tmc-langs-framework</artifactId>
38+
<version>${project.version}</version>
39+
<type>jar</type>
40+
</dependency>
41+
</dependencies>
42+
<build>
43+
<plugins>
44+
<plugin>
45+
<groupId>org.apache.maven.plugins</groupId>
46+
<artifactId>maven-surefire-plugin</artifactId>
47+
<version>2.18.1</version>
48+
<configuration>
49+
<excludes>
50+
<exclude>**/make-project/**/*</exclude>
51+
<exclude>**/eRror/**/*</exclude>
52+
</excludes>
53+
</configuration>
54+
</plugin>
55+
<plugin>
56+
<groupId>org.apache.maven.plugins</groupId>
57+
<artifactId>maven-jar-plugin</artifactId>
58+
<version>2.6</version>
59+
<configuration>
60+
<outputDirectory>${tmc.basedir}</outputDirectory>
61+
<archive>
62+
<manifest>
63+
<addClasspath>true</addClasspath>
64+
<classpathPrefix>lib/</classpathPrefix>
65+
</manifest>
66+
</archive>
67+
</configuration>
68+
</plugin>
69+
</plugins>
70+
</build>
71+
</project>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
2+
package fi.helsinki.cs.tmc.langs.qmake;
3+
4+
import fi.helsinki.cs.tmc.langs.domain.TestResult;
5+
6+
import com.google.common.collect.ImmutableList;
7+
8+
import java.util.List;
9+
10+
public final class QTestCase {
11+
12+
private String name;
13+
private boolean passed;
14+
private String message;
15+
private List<String> points;
16+
17+
/**
18+
* Create a test case for QT tests.
19+
*/
20+
public QTestCase(String name, boolean passed, String message, List<String> points) {
21+
this.name = name;
22+
this.passed = passed;
23+
this.message = message;
24+
this.points = points;
25+
}
26+
27+
/**
28+
* Get the test result of this test case.
29+
*/
30+
public TestResult getTestResult() {
31+
String msg = message;
32+
33+
ImmutableList<String> trace = ImmutableList.of();
34+
ImmutableList<String> points = ImmutableList.of();
35+
if (this.points != null) {
36+
points = ImmutableList.copyOf(this.points);
37+
}
38+
39+
return new TestResult(name, passed, points, msg, trace);
40+
}
41+
42+
public String getName() {
43+
return name;
44+
}
45+
46+
public boolean getResult() {
47+
return passed;
48+
}
49+
50+
public String getMessage() {
51+
return message;
52+
}
53+
54+
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package fi.helsinki.cs.tmc.langs.qmake;
2+
3+
import fi.helsinki.cs.tmc.langs.domain.RunResult;
4+
import fi.helsinki.cs.tmc.langs.domain.RunResult.Status;
5+
import fi.helsinki.cs.tmc.langs.domain.TestResult;
6+
7+
import com.google.common.collect.ImmutableList;
8+
import com.google.common.collect.ImmutableMap;
9+
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
import org.w3c.dom.Document;
14+
import org.w3c.dom.Element;
15+
import org.w3c.dom.NodeList;
16+
17+
import org.xml.sax.InputSource;
18+
import org.xml.sax.SAXException;
19+
20+
import java.io.FileInputStream;
21+
import java.io.IOException;
22+
import java.io.InputStream;
23+
import java.io.InputStreamReader;
24+
import java.io.Reader;
25+
import java.nio.file.Path;
26+
import java.util.ArrayList;
27+
import java.util.List;
28+
29+
import javax.xml.parsers.DocumentBuilder;
30+
import javax.xml.parsers.DocumentBuilderFactory;
31+
import javax.xml.parsers.ParserConfigurationException;
32+
33+
public final class QTestResultParser {
34+
35+
private static final String DOC_NULL_ERROR_MESSAGE = "Failed to parse test results";
36+
private static final String SAX_PARSER_ERROR = "SAX parser error occured";
37+
private static final String PARSING_DONE_MESSAGE = "Qt test cases parsed.";
38+
39+
private static final Logger log = LoggerFactory.getLogger(QTestResultParser.class);
40+
41+
private final List<TestResult> tests;
42+
43+
public QTestResultParser(Path testResults) {
44+
this.tests = parseTestCases(testResults);
45+
}
46+
47+
private List<TestResult> parseTestCases(Path testOutput) {
48+
Document doc;
49+
try {
50+
doc = prepareDocument(testOutput);
51+
} catch (ParserConfigurationException | IOException e) {
52+
log.error("Unexpected exception, could not parse Qt testcases.", e);
53+
return new ArrayList<>();
54+
}
55+
56+
NodeList nodeList = doc.getElementsByTagName("TestFunction");
57+
List<TestResult> cases = createQtTestResults(nodeList);
58+
59+
log.info(PARSING_DONE_MESSAGE);
60+
61+
return cases;
62+
}
63+
64+
private Document prepareDocument(Path testOutput)
65+
throws ParserConfigurationException, IOException {
66+
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
67+
DocumentBuilder documentBuilder = dbFactory.newDocumentBuilder();
68+
documentBuilder.setErrorHandler(null); // Silence logging
69+
dbFactory.setValidating(false);
70+
71+
InputStream inputStream = new FileInputStream(testOutput.toFile());
72+
Reader reader = new InputStreamReader(inputStream, "UTF-8");
73+
InputSource is = new InputSource(reader);
74+
is.setEncoding("UTF-8");
75+
76+
Document doc = null;
77+
try {
78+
doc = documentBuilder.parse(is);
79+
} catch (SAXException ex) {
80+
log.info(SAX_PARSER_ERROR);
81+
log.info(ex.toString());
82+
}
83+
84+
if (doc == null) {
85+
log.info(DOC_NULL_ERROR_MESSAGE);
86+
throw new IllegalStateException(DOC_NULL_ERROR_MESSAGE);
87+
}
88+
89+
doc.getDocumentElement().normalize();
90+
91+
return doc;
92+
}
93+
94+
/**
95+
* Parses Qt testlib XML output, as generated with -o filename.xml,xml.
96+
*
97+
* Points are mapped to test cases with Message node type 'qinfo'. These
98+
* messages contain: prefix "TMC:" test case name: "test_function_name"
99+
* points separated by period: ".1"
100+
*
101+
* With failing testcase assertions, there will be an Incident node with
102+
* type "fail" and Description node with the failed assertion message.
103+
*
104+
* When the testcase assertion(s) has passed, there will be an Incident node
105+
* with type "pass" and no Description node.
106+
*
107+
* <TestFunction name="test_function_name">
108+
* <Message type="qinfo" file="" line="0">
109+
* <Description><![CDATA[TMC:test_function_name.1]]></Description>
110+
* </Message>
111+
* <Incident type="fail" file="test_source.cpp" line="420">
112+
* <Description><![CDATA['!strcmp(hello_msg(), "Helo, world!" )' returned FALSE. ()]]></Description>
113+
* </Incident>
114+
* <Duration msecs="0.135260"/>
115+
* </TestFunction>
116+
*
117+
*/
118+
private List<TestResult> createQtTestResults(NodeList nodeList) {
119+
List<TestResult> cases = new ArrayList<>();
120+
121+
for (int i = 0; i < nodeList.getLength(); i++) {
122+
Element testcase = (Element) nodeList.item(i);
123+
List<String> points = parsePoints(testcase);
124+
125+
if (points.isEmpty()) {
126+
// No points == not a TMC testcase
127+
continue;
128+
}
129+
130+
Element incident = (Element) testcase.getElementsByTagName("Incident").item(0);
131+
132+
String id = testcase.getAttribute("name");
133+
boolean passed = incident.getAttribute("type").equals("pass");
134+
String msg = "";
135+
136+
// Get the assertion error if testcase failed
137+
if (!passed) {
138+
Element desc = (Element) incident.getElementsByTagName("Description").item(0);
139+
msg = desc.getTextContent();
140+
}
141+
142+
ImmutableList<String> trace = ImmutableList.of();
143+
cases.add(new TestResult(id, passed, ImmutableList.copyOf(points), msg, trace));
144+
}
145+
146+
return cases;
147+
}
148+
149+
/**
150+
*
151+
* Parse potential points from testcase.
152+
*
153+
*/
154+
private List<String> parsePoints(Element testcase) {
155+
List<String> points = new ArrayList<>();
156+
NodeList messages = testcase.getElementsByTagName("Message");
157+
for (int i = 0; i < messages.getLength(); i++) {
158+
Element message = (Element) messages.item(i);
159+
Element desc = (Element) message.getElementsByTagName("Description").item(0);
160+
String text = desc.getTextContent();
161+
if (text.matches("^(TMC:.*)")) {
162+
String[] split = text.split("\\.");
163+
points.add(split[1]);
164+
}
165+
}
166+
167+
return points;
168+
}
169+
170+
/**
171+
* Returns the test results of the tests in this file.
172+
*/
173+
public List<TestResult> getTestResults() {
174+
return this.tests;
175+
}
176+
177+
/**
178+
* Returns the combined status of the tests in this file.
179+
*/
180+
public Status getResultStatus() {
181+
for (TestResult result : getTestResults()) {
182+
if (!result.isSuccessful()) {
183+
return Status.TESTS_FAILED;
184+
}
185+
}
186+
187+
return Status.PASSED;
188+
}
189+
190+
/**
191+
* Returns the run result of this file.
192+
*/
193+
public RunResult result() {
194+
return new RunResult(
195+
getResultStatus(),
196+
ImmutableList.copyOf(getTestResults()),
197+
new ImmutableMap.Builder<String, byte[]>().build());
198+
}
199+
}

0 commit comments

Comments
 (0)