2727import org .slf4j .LoggerFactory ;
2828
2929import java .io .File ;
30+ import java .io .IOException ;
3031import java .nio .charset .StandardCharsets ;
32+ import java .nio .file .FileVisitResult ;
3133import java .nio .file .Files ;
3234import java .nio .file .Path ;
3335import java .nio .file .Paths ;
36+ import java .nio .file .SimpleFileVisitor ;
37+ import java .nio .file .attribute .BasicFileAttributes ;
3438import java .util .ArrayList ;
3539import java .util .Arrays ;
40+ import java .util .HashMap ;
3641import java .util .List ;
3742import java .util .Locale ;
3843import java .util .Map ;
44+ import java .util .regex .MatchResult ;
45+ import java .util .regex .Matcher ;
46+ import java .util .regex .Pattern ;
3947
4048public final class QmakePlugin extends AbstractLanguagePlugin {
4149
42- private static final Path TEST_DIR = Paths .get ("test" );
4350 private static final Path TMC_TEST_RESULTS = Paths .get ("tmc_test_results.xml" );
4451
52+ // POINT(exercise_name, 1)
53+ private static final Pattern POINT_PATTERN
54+ = Pattern .compile ("POINT\\ (\\ s*(\\ w+),\\ s*(\\ w+)\\ s*\\ )\\ s*;" );
55+ private static final Pattern COMMENT_PATTERN
56+ = Pattern .compile ("(^[^\" \\ r\\ n]*\\ /\\ *{1,2}.*?\\ *\\ /|(^[^\" \\ r\\ n]*\\ /\\ /[^\\ r\\ n]*))" , Pattern .MULTILINE | Pattern .DOTALL );
57+
4558 private static final RunResult EMPTY_FAILURE
4659 = new RunResult (
4760 Status .COMPILE_FAILED ,
@@ -71,7 +84,8 @@ public String getPluginName() {
7184 * be named after the directory.
7285 */
7386 private Path getProFile (Path basePath ) {
74- return basePath .resolve (new File (basePath .toFile ().getName () + ".pro" ).toPath ());
87+ Path proFile = new File (basePath .toFile ().getName () + ".pro" ).toPath ();
88+ return basePath .resolve (proFile );
7589 }
7690
7791 @ Override
@@ -81,43 +95,13 @@ public Optional<ExerciseDesc> scanExercise(Path path, String exerciseName) {
8195 return Optional .absent ();
8296 }
8397
84- try {
85- runTests (path );
86- } catch (Exception e ) {
87- log .error ("Failed to run tests {}" , e );
98+ Optional <ImmutableList <TestDesc >> tests = availablePoints (path );
99+ if (!tests .isPresent ()) {
100+ log .error ("Failed to scan exercise due to parsing error." );
88101 return Optional .absent ();
89102 }
90103
91- return Optional .of (parseExerciseDesc (path , exerciseName ));
92- }
93-
94- private ExerciseDesc parseExerciseDesc (Path testResults , String exerciseName ) {
95- List <TestDesc > tests = createTestDescs (testResults );
96- return new ExerciseDesc (exerciseName , ImmutableList .copyOf (tests ));
97- }
98-
99- private List <TestDesc > createTestDescs (Path testResults ) {
100- List <TestDesc > tests = new ArrayList <>();
101- List <String > addedTests = new ArrayList <>();
102-
103- List <TestResult > testCases = readTestResults (testResults );
104-
105- for (int i = 0 ; i < testCases .size (); i ++) {
106- TestResult testCase = testCases .get (i );
107-
108- String testClass = testCase .getName ();
109- String testMethod = testCase .getName ();
110- String testName = testClass + "." + testMethod ;
111-
112- List <String > points = Arrays .asList (testCase .getMessage ().split ("" ));
113-
114- if (!addedTests .contains (testName )) {
115- tests .add (new TestDesc (testName , ImmutableList .copyOf (points )));
116- addedTests .add (testName );
117- }
118- }
119-
120- return tests ;
104+ return Optional .of (new ExerciseDesc (exerciseName , ImmutableList .copyOf (tests .get ())));
121105 }
122106
123107 @ Override
@@ -183,13 +167,6 @@ private Optional<RunResult> build(Path path) {
183167 return Optional .absent ();
184168 }
185169
186- private List <TestResult > readTestResults (Path testPath ) {
187- Path baseTestPath = testPath .toAbsolutePath ().resolve (TEST_DIR );
188- Path testResults = baseTestPath .resolve (TMC_TEST_RESULTS );
189-
190- return new QTestResultParser (testResults ).getTestResults ();
191- }
192-
193170 @ Override
194171 public ValidationResult checkCodeStyle (Path path , Locale locale ) {
195172 return new ValidationResult () {
@@ -263,4 +240,72 @@ private RunResult filledFailure(ProcessResult processResult) {
263240 .<String , byte []>build ();
264241 return new RunResult (Status .COMPILE_FAILED , ImmutableList .<TestResult >of (), logs );
265242 }
243+
244+ /**
245+ * TODO: This is copy paste from tmc-langs regex branch. Regex branch method
246+ * 'availablePoints' does not parse test names at this time.
247+ *
248+ * POINT_PATTERN matches the #define macro in the tests, for example:
249+ *
250+ * POINT(test_name2, suite_point);
251+ *
252+ * POINT(test_name, 1);
253+ *
254+ * etc.
255+ */
256+ public Optional <ImmutableList <TestDesc >> availablePoints (final Path rootPath ) {
257+ return findAvailablePoints (rootPath , POINT_PATTERN , COMMENT_PATTERN , ".cpp" );
258+ }
259+
260+ protected Optional <ImmutableList <TestDesc >> findAvailablePoints (final Path rootPath ,
261+ Pattern pattern , Pattern commentPattern , String suffix ) {
262+ Map <String , List <String >> tests = new HashMap <>();
263+ List <TestDesc > descs = new ArrayList <>();
264+ try {
265+ final List <Path > potentialPointFiles = getPotentialPointFiles (rootPath , suffix );
266+ for (Path p : potentialPointFiles ) {
267+
268+ String contents = new String (Files .readAllBytes (p ), "UTF-8" );
269+ String withoutComments = commentPattern .matcher (contents ).replaceAll ("" );
270+ Matcher matcher = pattern .matcher (withoutComments );
271+ while (matcher .find ()) {
272+ MatchResult matchResult = matcher .toMatchResult ();
273+ String testName = matchResult .group (1 ).trim ();
274+ String point = matchResult .group (2 ).trim ();
275+ tests .putIfAbsent (testName , new ArrayList <String >());
276+ tests .get (testName ).add (point );
277+ }
278+ }
279+ for (String testName : tests .keySet ()) {
280+ List <String > points = tests .getOrDefault (testName , new ArrayList <String >());
281+ descs .add (new TestDesc (testName , ImmutableList .<String >copyOf (points )));
282+ }
283+ return Optional .of (ImmutableList .copyOf (descs ));
284+ } catch (IOException e ) {
285+ // We could scan the exercise but that could be a security risk
286+ return Optional .absent ();
287+ }
288+ }
289+
290+ private List <Path > getPotentialPointFiles (final Path rootPath , final String suffix )
291+ throws IOException {
292+ final StudentFilePolicy studentFilePolicy = getStudentFilePolicy (rootPath );
293+ final List <Path > potentialPointFiles = new ArrayList <>();
294+
295+ Files .walkFileTree (rootPath , new SimpleFileVisitor <Path >() {
296+ @ Override
297+ public FileVisitResult visitFile (Path path , BasicFileAttributes attrs )
298+ throws IOException {
299+ if (studentFilePolicy .isStudentFile (path , rootPath )) {
300+ return FileVisitResult .SKIP_SUBTREE ;
301+ }
302+ if (!Files .isDirectory (path ) && path .toString ().endsWith (suffix )) {
303+ potentialPointFiles .add (path );
304+ }
305+ return FileVisitResult .CONTINUE ;
306+ }
307+ });
308+
309+ return potentialPointFiles ;
310+ }
266311}
0 commit comments