1
1
package fi .helsinki .cs .tmc .langs .r ;
2
2
3
-
4
3
import fi .helsinki .cs .tmc .langs .AbstractLanguagePlugin ;
4
+ import fi .helsinki .cs .tmc .langs .abstraction .Strategy ;
5
+ import fi .helsinki .cs .tmc .langs .abstraction .ValidationError ;
5
6
import fi .helsinki .cs .tmc .langs .abstraction .ValidationResult ;
6
7
import fi .helsinki .cs .tmc .langs .domain .ExerciseBuilder ;
7
8
import fi .helsinki .cs .tmc .langs .domain .ExerciseDesc ;
8
9
import fi .helsinki .cs .tmc .langs .domain .RunResult ;
10
+ import fi .helsinki .cs .tmc .langs .domain .SpecialLogs ;
9
11
import fi .helsinki .cs .tmc .langs .domain .TestDesc ;
12
+ import fi .helsinki .cs .tmc .langs .domain .TestResult ;
10
13
import fi .helsinki .cs .tmc .langs .io .StudentFilePolicy ;
11
14
import fi .helsinki .cs .tmc .langs .io .sandbox .StudentFileAwareSubmissionProcessor ;
12
15
import fi .helsinki .cs .tmc .langs .io .zip .StudentFileAwareUnzipper ;
13
16
import fi .helsinki .cs .tmc .langs .io .zip .StudentFileAwareZipper ;
14
-
15
17
import fi .helsinki .cs .tmc .langs .utils .ProcessRunner ;
16
18
17
-
18
19
import com .google .common .base .Optional ;
19
20
import com .google .common .collect .ImmutableList ;
20
-
21
+ import com .google .common .collect .ImmutableMap ;
22
+ import com .google .common .collect .Maps ;
21
23
22
24
import org .apache .commons .lang3 .ArrayUtils ;
23
25
import org .apache .commons .lang3 .SystemUtils ;
26
+ import org .apache .commons .lang3 .exception .ExceptionUtils ;
24
27
25
28
import org .slf4j .Logger ;
26
29
import org .slf4j .LoggerFactory ;
27
30
31
+ import java .io .File ;
28
32
import java .io .IOException ;
33
+ import java .nio .file .Files ;
29
34
import java .nio .file .Path ;
35
+ import java .nio .file .Paths ;
36
+ import java .util .ArrayList ;
37
+ import java .util .HashMap ;
38
+ import java .util .List ;
30
39
import java .util .Locale ;
40
+ import java .util .Map ;
31
41
42
+ public final class RPlugin extends AbstractLanguagePlugin {
32
43
33
-
34
- public class RPlugin extends AbstractLanguagePlugin {
35
-
36
- // Various static final Path-variables for filepaths
37
- // to various folders and files in a R exercise project here
44
+ /**
45
+ * R folder contains the actual R files used in the
46
+ * project/package. It is automatically included when creating a
47
+ * R package but now when making a regular project in RStudio.
48
+ */
38
49
private static final Path R_FOLDER_PATH = Paths .get ("R" );
50
+
51
+ /**
52
+ * test/testthat folder contains the unit testing
53
+ * files which use the testThat library for the R project.
54
+ */
39
55
private static final Path TEST_FOLDER_PATH = Paths .get ("tests" );
40
56
private static final Path TESTTHAT_FOLDER_PATH = Paths .get ("testthat" );
41
- private static final Path TESTTHAT_FILE_PATH = Paths .get ("testthat.R" );
42
- private static final Path DESCRIPTION_PATH = Paths .get ("DESCRIPTION" );
43
- private static final Path RESULT_R_PATH = Paths .get ("result.R" );
44
-
45
57
46
-
47
- // Various static final String-variables for
48
- // error messages related to parsing and running R tests here
49
58
private static final String CANNOT_RUN_TESTS_MESSAGE = "Failed to run tests." ;
50
59
private static final String CANNOT_PARSE_TEST_RESULTS_MESSAGE = "Failed to read test results." ;
51
60
private static final String CANNOT_SCAN_EXERCISE_MESSAGE = "Failed to scan exercise." ;
@@ -62,33 +71,14 @@ public RPlugin() {
62
71
new StudentFileAwareUnzipper ());
63
72
}
64
73
74
+ /**
75
+ * NOTE: Files.exists does not seem to be able to verify the R and
76
+ * testthat folder's existence if they are empty.
77
+ */
65
78
@ Override
66
79
public boolean isExerciseTypeCorrect (Path path ) {
67
80
return Files .exists (path .resolve (R_FOLDER_PATH ))
68
- || Files .exists (path .resolve (TEST_FOLDER_PATH ).resolve (TESTTHAT_FOLDER_PATH ))
69
- || Files .exists (path .resolve (TEST_FOLDER_PATH ).resolve (TESTTHAT_FILE_PATH ))
70
- || Files .exists (path .resolve (DESCRIPTION_PATH ));
71
- /*
72
- R folder contains the actual R files used in the
73
- project/package. It is automatically included when creating a
74
- R package but now when making a regular project in RStudio.
75
-
76
- test/testthat folder contains the unit testing
77
- files which use the testThat library for the R project.
78
-
79
- DESCRIPTION file contains package information.
80
- Included automatically when making a new package, but not
81
- included when making a regular project in RStudio.
82
-
83
- .RHistory file contains the history of executed code on
84
- the R terminal. Generated after running code on the R
85
- terminal for the first time.
86
-
87
- tmc/result.R contains the call to tmcRtestrunner's runTests function.
88
-
89
- NOTE: Files.exists does not seem to be able to verify the R and
90
- testthat folder's existence if they are empty.
91
- */
81
+ || Files .exists (path .resolve (TEST_FOLDER_PATH ).resolve (TESTTHAT_FOLDER_PATH ));
92
82
}
93
83
94
84
@ Override
@@ -104,43 +94,67 @@ public String getPluginName() {
104
94
@ Override
105
95
public Optional <ExerciseDesc > scanExercise (Path path , String exerciseName ) {
106
96
ProcessRunner runner = new ProcessRunner (this .getAvailablePointsCommand (), path );
97
+
107
98
try {
108
99
runner .call ();
109
100
} catch (Exception e ) {
110
- System .out .println (e );
111
101
log .error (CANNOT_SCAN_EXERCISE_MESSAGE , e );
102
+ return Optional .absent ();
112
103
}
104
+
113
105
try {
114
106
ImmutableList <TestDesc > testDescs = new RExerciseDescParser (path ).parse ();
115
107
return Optional .of (new ExerciseDesc (exerciseName , testDescs ));
116
108
} catch (IOException e ) {
117
109
log .error (CANNOT_PARSE_EXERCISE_DESCRIPTION_MESSAGE , e );
118
110
}
111
+
119
112
return Optional .absent ();
120
113
}
121
114
122
115
@ Override
123
116
public RunResult runTests (Path path ) {
124
-
125
117
ProcessRunner runner = new ProcessRunner (getTestCommand (), path );
118
+
126
119
try {
127
120
runner .call ();
128
121
} catch (Exception e ) {
129
122
log .error (CANNOT_RUN_TESTS_MESSAGE , e );
123
+ return getGenericErrorRunResult (e );
130
124
}
131
125
132
126
try {
133
127
return new RTestResultParser (path ).parse ();
134
128
} catch (IOException e ) {
135
129
log .error (CANNOT_PARSE_TEST_RESULTS_MESSAGE , e );
130
+ return getGenericErrorRunResult (e );
136
131
}
137
- return null ;
132
+ }
133
+
134
+ private RunResult getGenericErrorRunResult (Throwable exception ) {
135
+ Map <String , byte []> logMap = new HashMap <>();
136
+ byte [] stackTraceAsByteArray = ExceptionUtils .getStackTrace (exception ).getBytes ();
137
+ logMap .put (SpecialLogs .GENERIC_ERROR_MESSAGE , stackTraceAsByteArray );
138
+
139
+ ImmutableMap <String , byte []> logs = ImmutableMap .copyOf (logMap );
140
+
141
+ return new RunResult (RunResult .Status .GENERIC_ERROR ,
142
+ ImmutableList .copyOf (new ArrayList <TestResult >()), logs );
138
143
}
139
144
140
145
@ Override
141
- public ValidationResult checkCodeStyle (Path path , Locale messageLocale ) throws UnsupportedOperationException {
142
- // TO DO
143
- return null ;
146
+ public ValidationResult checkCodeStyle (Path path , Locale messageLocale ) {
147
+ return new ValidationResult () {
148
+ @ Override
149
+ public Strategy getStrategy () {
150
+ return Strategy .DISABLED ;
151
+ }
152
+
153
+ @ Override
154
+ public Map <File , List <ValidationError >> getValidationErrors () {
155
+ return Maps .newHashMap ();
156
+ }
157
+ };
144
158
}
145
159
146
160
public String [] getTestCommand () {
@@ -165,9 +179,10 @@ public String[] getAvailablePointsCommand() {
165
179
return ArrayUtils .addAll (command , args );
166
180
}
167
181
168
-
182
+ /**
183
+ * No operation for now. To be possibly implemented later: remove .Rdata, .Rhistory etc
184
+ */
169
185
@ Override
170
186
public void clean (Path path ) {
171
- // TO DO
172
187
}
173
188
}
0 commit comments