Skip to content

Commit 29b9ab2

Browse files
kimmoalnygrenh
authored andcommitted
Fix path issues and point parsing (#90)
* Fix point parsing regex Points with a '.' were not parsed * Fix path issues, improve error handling/logging * Tests for point parsing * Fix checkstyle * Replace RuntimeExceptions with RunResults
1 parent 4e957df commit 29b9ab2

File tree

7 files changed

+141
-38
lines changed

7 files changed

+141
-38
lines changed

tmc-langs-qmake/src/main/java/fi/helsinki/cs/tmc/langs/qmake/QmakeBuildException.java

-11
This file was deleted.

tmc-langs-qmake/src/main/java/fi/helsinki/cs/tmc/langs/qmake/QmakePlugin.java

+45-18
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.nio.charset.StandardCharsets;
3232
import java.nio.file.FileVisitResult;
3333
import java.nio.file.Files;
34+
import java.nio.file.LinkOption;
3435
import java.nio.file.Path;
3536
import java.nio.file.Paths;
3637
import java.nio.file.SimpleFileVisitor;
@@ -49,9 +50,9 @@ public final class QmakePlugin extends AbstractLanguagePlugin {
4950

5051
private static final Path TMC_TEST_RESULTS = Paths.get("tmc_test_results.xml");
5152

52-
// Finds pattern 'POINT(exercise_name, 1)'
53+
// Finds pattern 'POINT(exercise_name, 1.1)'
5354
private static final Pattern POINT_PATTERN
54-
= Pattern.compile("POINT\\(\\s*(\\w+),\\s*(\\w+)\\s*\\)\\s*;");
55+
= Pattern.compile("POINT\\(\\s*(\\w+),\\s*([^\\s|\\)]+)\\s*\\)\\s*;");
5556
// Pattern to find comments
5657
private static final Pattern COMMENT_PATTERN
5758
= Pattern.compile("(^[^\"\\r\\n]*\\/\\*{1,2}.*?\\*\\/"
@@ -79,8 +80,9 @@ public String getPluginName() {
7980
* Resolve the exercise .pro file from exercise directory. The file should
8081
* be named after the directory.
8182
*/
82-
private Path getProFile(Path basePath) {
83-
return Paths.get(basePath.toString() + "/" + basePath.getFileName() + ".pro");
83+
private Path getProFile(Path basePath) throws IOException {
84+
Path fullPath = basePath.toRealPath(LinkOption.NOFOLLOW_LINKS);
85+
return fullPath.resolve(fullPath.getFileName() + ".pro");
8486
}
8587

8688
@Override
@@ -101,7 +103,11 @@ public Optional<ExerciseDesc> scanExercise(Path path, String exerciseName) {
101103

102104
@Override
103105
public boolean isExerciseTypeCorrect(Path path) {
104-
return Files.isRegularFile(getProFile(path));
106+
try {
107+
return Files.isRegularFile(getProFile(path));
108+
} catch (IOException e) {
109+
return false;
110+
}
105111
}
106112

107113
/**
@@ -121,31 +127,45 @@ protected StudentFilePolicy getStudentFilePolicy(Path projectPath) {
121127

122128
@Override
123129
public RunResult runTests(Path path) {
124-
Path shadowDir = makeShadowBuildDir(path);
130+
Path fullPath;
131+
try {
132+
fullPath = path.toRealPath(LinkOption.NOFOLLOW_LINKS);
133+
} catch (IOException e) {
134+
log.error("Exercise directory not found", e);
135+
return filledFailure(Status.GENERIC_ERROR, "Exercise directory not found");
136+
}
137+
138+
Path shadowDir;
139+
try {
140+
shadowDir = makeShadowBuildDir(fullPath);
141+
} catch (IOException e) {
142+
log.error("Preparing exercise failed", e);
143+
return filledFailure(Status.GENERIC_ERROR, "Could not create build directory");
144+
}
125145

126146
try {
127147
ProcessResult qmakeBuild = buildWithQmake(shadowDir);
128148
if (qmakeBuild.statusCode != 0) {
129149
log.error("Building project with qmake failed: {}", qmakeBuild.errorOutput);
130-
return filledFailure(qmakeBuild.errorOutput);
150+
return filledFailure(Status.COMPILE_FAILED, qmakeBuild.errorOutput);
131151
}
132152
} catch (IOException | InterruptedException e) {
133153
log.error("Building project with qmake failed", e);
134-
throw new QmakeBuildException(e);
154+
return filledFailure(Status.GENERIC_ERROR, "Building project with qmake failed");
135155
}
136156

137157
try {
138158
ProcessResult makeBuild = buildWithMake(shadowDir);
139159
if (makeBuild.statusCode != 0) {
140160
log.error("Building project with make failed: {}", makeBuild.errorOutput);
141-
return filledFailure(makeBuild.errorOutput);
161+
return filledFailure(Status.COMPILE_FAILED, makeBuild.errorOutput);
142162
}
143163
} catch (IOException | InterruptedException e) {
144164
log.error("Building project with make failed", e);
145-
throw new QmakeBuildException(e);
165+
return filledFailure(Status.GENERIC_ERROR, "Building project with make failed");
146166
}
147167

148-
Path testResults = path.resolve(TMC_TEST_RESULTS);
168+
Path testResults = shadowDir.resolve(TMC_TEST_RESULTS);
149169

150170
String target = "check";
151171
String config = "TESTARGS=-o " + testResults.toString() + ",xml";
@@ -158,11 +178,11 @@ public RunResult runTests(Path path) {
158178

159179
if (!Files.exists(testResults)) {
160180
log.error("Failed to get test output at {}", testResults);
161-
return filledFailure(testRun.output);
181+
return filledFailure(Status.GENERIC_ERROR, testRun.output);
162182
}
163183
} catch (IOException | InterruptedException e) {
164184
log.error("Testing with make check failed", e);
165-
throw new QmakeBuildException(e);
185+
return filledFailure(Status.GENERIC_ERROR, "Testing with make check failed");
166186
}
167187

168188
QTestResultParser parser = new QTestResultParser();
@@ -185,11 +205,18 @@ public Map<File, List<ValidationError>> getValidationErrors() {
185205
};
186206
}
187207

188-
private Path makeShadowBuildDir(Path dir) {
189-
File buildDir = dir.resolve("build").toFile();
208+
private Path makeShadowBuildDir(Path dir) throws IOException {
209+
Path shadowPath = dir.resolve("build");
210+
if (Files.exists(shadowPath)) {
211+
log.info("Shadow dir already exists at {}", shadowPath);
212+
return shadowPath;
213+
}
214+
215+
File buildDir = shadowPath.toFile();
216+
190217
log.info("Making shadow build dir to {}", buildDir.toPath());
191218
if (!buildDir.mkdirs()) {
192-
throw new RuntimeException(
219+
throw new IOException(
193220
"Unable to create shadow build directory: "
194221
+ buildDir.toPath());
195222
}
@@ -231,13 +258,13 @@ private ProcessResult run(String[] command, Path dir) throws IOException, Interr
231258
return runner.call();
232259
}
233260

234-
private RunResult filledFailure(String output) {
261+
private RunResult filledFailure(Status status, String output) {
235262
byte[] errorOutput = output.getBytes(StandardCharsets.UTF_8);
236263
ImmutableMap<String, byte[]> logs
237264
= new ImmutableMap.Builder()
238265
.put(SpecialLogs.COMPILER_OUTPUT, errorOutput)
239266
.<String, byte[]>build();
240-
return new RunResult(Status.COMPILE_FAILED, ImmutableList.<TestResult>of(), logs);
267+
return new RunResult(status, ImmutableList.<TestResult>of(), logs);
241268
}
242269

243270
/**

tmc-langs-qmake/src/test/java/fi/helsinki/cs/tmc/langs/qmake/QmakePluginTest.java

+85-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import fi.helsinki.cs.tmc.langs.domain.ExerciseDesc;
88
import fi.helsinki.cs.tmc.langs.domain.RunResult;
99
import fi.helsinki.cs.tmc.langs.domain.TestDesc;
10+
import fi.helsinki.cs.tmc.langs.domain.TestResult;
1011
import fi.helsinki.cs.tmc.langs.utils.TestUtils;
1112

1213
import com.google.common.base.Optional;
@@ -20,6 +21,7 @@ public class QmakePluginTest {
2021
private final QmakePlugin qmakePlugin;
2122

2223
public QmakePluginTest() {
24+
TestUtils.skipIfNotAvailable("qmake");
2325
qmakePlugin = new QmakePlugin();
2426
}
2527

@@ -49,6 +51,37 @@ public void testQtTestsPassingWithMultipleLib() {
4951
runTests("passing_multiple_lib"));
5052
}
5153

54+
@Test
55+
public void testQtTestsPassingWithMultipleLibSamePoints() {
56+
RunResult result = runTests(TestUtils.getPath(getClass(),
57+
"passing_multiple_lib_same_point"));
58+
assertEquals("Tests passing with multiple libraries and same points",
59+
RunResult.Status.PASSED, result.status);
60+
61+
assertEquals(3, result.testResults.size());
62+
63+
TestResult test1 = result.testResults.get(0);
64+
assertEquals(test1.getName(), "test_function_one_here");
65+
assertTrue(test1.isSuccessful());
66+
assertEquals(2, test1.points.size());
67+
assertEquals("testPoint1", test1.points.get(0));
68+
assertEquals("testPoint1.1", test1.points.get(1));
69+
70+
TestResult test2 = result.testResults.get(1);
71+
assertEquals(test2.getName(), "test_function_two_here");
72+
assertTrue(test2.isSuccessful());
73+
assertEquals(2, test2.points.size());
74+
assertEquals("testPoint1", test2.points.get(0));
75+
assertEquals("testPoint1.2", test2.points.get(1));
76+
77+
TestResult test3 = result.testResults.get(2);
78+
assertEquals(test3.getName(), "test_function_two_here_2");
79+
assertTrue(test3.isSuccessful());
80+
assertEquals(2, test3.points.size());
81+
assertEquals("testPoint1", test3.points.get(0));
82+
assertEquals("testPoint1.3", test3.points.get(1));
83+
}
84+
5285
@Test
5386
public void testQtBuildFailingWithSingleLib() {
5487
assertEquals("Student source not compiling with compiling single lib",
@@ -58,9 +91,24 @@ public void testQtBuildFailingWithSingleLib() {
5891

5992
@Test
6093
public void testQtFailingSingleLib() {
94+
RunResult result = runTests(TestUtils.getPath(getClass(), "failing_single_lib"));
6195
assertEquals("Tests failing with single library",
6296
RunResult.Status.TESTS_FAILED,
63-
runTests("failing_single_lib"));
97+
result.status);
98+
99+
assertEquals(2, result.testResults.size());
100+
101+
TestResult test1 = result.testResults.get(0);
102+
assertEquals(test1.getName(), "test_function_one_here");
103+
assertTrue(test1.isSuccessful());
104+
assertEquals(1, test1.points.size());
105+
assertEquals("1", test1.points.get(0));
106+
107+
TestResult test2 = result.testResults.get(1);
108+
assertEquals(test2.getName(), "test_function_two_here");
109+
assertFalse(test2.isSuccessful());
110+
assertEquals(1, test2.points.size());
111+
assertEquals("2", test2.points.get(0));
64112
}
65113

66114
@Test
@@ -127,6 +175,38 @@ public void testScanExerciseWithPassingSamePoints() {
127175
assertEquals("2", test2.points.get(0));
128176
}
129177

178+
@Test
179+
public void testScanExerciseWithPartialPoints() {
180+
Optional<ExerciseDesc> optional = scanExercise("passing_multiple_lib_same_point");
181+
assertTrue(optional.isPresent());
182+
183+
ExerciseDesc desc = optional.get();
184+
185+
assertEquals("passing_multiple_lib_same_point", desc.name);
186+
187+
assertEquals(3, desc.tests.size());
188+
TestDesc test1 = desc.tests.get(2);
189+
TestDesc test2 = desc.tests.get(0);
190+
TestDesc test3 = desc.tests.get(1);
191+
192+
assertEquals("test_function_one_here", test1.name);
193+
assertEquals("test_function_two_here", test2.name);
194+
assertEquals("test_function_two_here_2", test3.name);
195+
196+
assertEquals(2, test1.points.size());
197+
assertEquals("testPoint1", test1.points.get(0));
198+
assertEquals("testPoint1.1", test1.points.get(1));
199+
200+
assertEquals(2, test2.points.size());
201+
assertEquals("testPoint1", test2.points.get(0));
202+
assertEquals("testPoint1.2", test2.points.get(1));
203+
204+
assertEquals(2, test2.points.size());
205+
assertEquals("testPoint1", test3.points.get(0));
206+
assertEquals("testPoint1.3", test3.points.get(1));
207+
208+
}
209+
130210
@Test
131211
public void testScanExerciseWithFailingSamePoints() {
132212
Optional<ExerciseDesc> optional = scanExercise("failing_single_lib_same_point");
@@ -179,6 +259,10 @@ public void testScanExerciseWithNonexistentProject() {
179259
assertFalse(optional.isPresent());
180260
}
181261

262+
private RunResult runTests(Path testExercise) {
263+
return qmakePlugin.runTests(testExercise);
264+
}
265+
182266
private RunResult.Status runTests(String testExercise) {
183267
return qmakePlugin.runTests(TestUtils.getPath(getClass(), testExercise)).status;
184268
}

tmc-langs-qmake/src/test/resources/passing_multiple_lib_same_point/passing_multiple_lib_same_point.pro

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
TEMPLATE = subdirs
22
SUBDIRS += \
3-
test_case_app \
4-
test_case_test_runner \
3+
src \
4+
test_case_test_runner \
55
test_case_lib \
66
test_case_lib2
77

8-
test_case_app.depends = test_case_lib \
9-
test_case_lib2
8+
src.depends = test_case_lib \
9+
test_case_lib2
1010
test_case_test_runner.depends = test_case_lib \
1111
test_case_lib2
1212

tmc-langs-qmake/src/test/resources/passing_multiple_lib_same_point/test_case_test_runner/test_case_test_runner.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ void test_case_test_runner::test_function_one_here() {
1515

1616
test_case_lib test_case;
1717

18-
POINT(test_function_one_here, 1);
18+
POINT(test_function_one_here, testPoint1);
19+
POINT(test_function_one_here, testPoint1.1);
1920
QVERIFY(!strcmp(test_case.piece_of_string(), "Hello, world!"));
2021

2122
}
@@ -24,15 +25,17 @@ void test_case_test_runner::test_function_two_here() {
2425

2526
test_case_lib2 test_case;
2627

27-
POINT(test_function_two_here, 2);
28-
QVERIFY(test_case.adding_ints(666, 1337) == 2003);
28+
POINT(test_function_two_here, testPoint1);
29+
POINT(test_function_two_here, testPoint1.2);
30+
QVERIFY(test_case.adding_ints(666, 1337) == 2003);
2931

3032
}
3133

3234
void test_case_test_runner::test_function_two_here_2() {
3335

3436
test_case_lib2 test_case;
3537

36-
POINT(test_function_two_here_2, 1);
38+
POINT(test_function_two_here_2, testPoint1);
39+
POINT(test_function_two_here_2, testPoint1.3);
3740
QVERIFY(test_case.adding_ints(-341, 428) == 87);
3841
}

0 commit comments

Comments
 (0)