Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

package fi.helsinki.cs.tmc.langs.r;

import fi.helsinki.cs.tmc.langs.domain.TestDesc;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;



class RExerciseDescParser {

private static Path RESULT_FILE = Paths.get(".available_points.json");
private static final TypeReference<Map<String, List<String>>> MAP_TYPE_REFERENCE =
new TypeReference<Map<String, List<String>>>() {};
private Path path;
private ObjectMapper mapper;

public RExerciseDescParser(Path path) {
this.path = path;
this.mapper = new ObjectMapper();
}

public ImmutableList<TestDesc> parse() throws IOException {
List<TestDesc> testDescs = new ArrayList<>();
byte[] json = Files.readAllBytes(path.resolve(RESULT_FILE));
Map<String, List<String>> parse = mapper.readValue(json, MAP_TYPE_REFERENCE);
for (String name : parse.keySet()) {
ImmutableList<String> points = ImmutableList.copyOf(parse.get(name));
testDescs.add(new TestDesc(name, points));
}
return ImmutableList.copyOf(testDescs);
}

}
79 changes: 69 additions & 10 deletions tmc-langs-r/src/main/java/fi/helsinki/cs/tmc/langs/r/RPlugin.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package fi.helsinki.cs.tmc.langs.r;


import fi.helsinki.cs.tmc.langs.AbstractLanguagePlugin;
import fi.helsinki.cs.tmc.langs.abstraction.ValidationResult;
import fi.helsinki.cs.tmc.langs.domain.ExerciseBuilder;
import fi.helsinki.cs.tmc.langs.domain.ExerciseDesc;
import fi.helsinki.cs.tmc.langs.domain.RunResult;
import fi.helsinki.cs.tmc.langs.domain.TestDesc;
import fi.helsinki.cs.tmc.langs.io.StudentFilePolicy;
import fi.helsinki.cs.tmc.langs.io.sandbox.StudentFileAwareSubmissionProcessor;
import fi.helsinki.cs.tmc.langs.io.sandbox.SubmissionProcessor;
import fi.helsinki.cs.tmc.langs.io.zip.StudentFileAwareUnzipper;
import fi.helsinki.cs.tmc.langs.io.zip.StudentFileAwareZipper;
import fi.helsinki.cs.tmc.langs.io.zip.Unzipper;
import fi.helsinki.cs.tmc.langs.io.zip.Zipper;
import fi.helsinki.cs.tmc.langs.python3.Python3TestResultParser;

import fi.helsinki.cs.tmc.langs.utils.ProcessRunner;


import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;


import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.SystemUtils;
Expand All @@ -24,17 +26,32 @@
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Locale;

public final class RPlugin extends AbstractLanguagePlugin {

// Various static final Path-variables for filepaths
// to various folders and files in a R exercise project here
private static final Path R_FOLDER_PATH = Paths.get("R");
private static final Path TEST_FOLDER_PATH = Paths.get("tests");
private static final Path TESTTHAT_FOLDER_PATH = Paths.get("testthat");
private static final Path TMC_FOLDER_PATH = Paths.get("tmc");
private static final Path DESCRIPTION_PATH = Paths.get("DESCRIPTION");
private static final Path RHISTORY_PATH = Paths.get(".RHistory");
private static final Path RESULT_R_PATH = Paths.get("result.R");

private static final String CANNOT_RUN_TESTS_MESSAGE = "Failed to run tests.";
private static final String CANNOT_PARSE_TEST_RESULTS_MESSAGE = "Failed to read test results.";
private static final String CANNOT_SCAN_EXERCISE_MESSAGE = "Failed to scan exercise.";
private static final String CANNOT_PARSE_EXERCISE_DESCRIPTION_MESSAGE =
"Failed to parse exercise description.";

// Various static final String-variables for
// error messages related to parsing and running R tests here

private static Logger log = LoggerFactory.getLogger(RPlugin.class);

public RPlugin() {
Expand All @@ -47,22 +64,57 @@ public RPlugin() {

@Override
public boolean isExerciseTypeCorrect(Path path) {
return false;
return Files.exists(path.resolve(R_FOLDER_PATH))
|| Files.exists(path.resolve(TEST_FOLDER_PATH).resolve(TESTTHAT_FOLDER_PATH))
|| Files.exists(path.resolve(DESCRIPTION_PATH))
|| Files.exists(path.resolve(RHISTORY_PATH))
|| Files.exists(path.resolve(TMC_FOLDER_PATH).resolve(RESULT_R_PATH));
/*
R folder contains the actual R files used in the
project/package. It is automatically included when creating a
R package but now when making a regular project in RStudio.

test/testthat folder contains the unit testing
files which use the testThat library for the R project.

DESCRIPTION file contains package information.
Included automatically when making a new package, but not
included when making a regular project in RStudio.

.RHistory file contains the history of executed code on
the R terminal. Generated after running code on the R
terminal for the first time.

tmc/result.R contains the call to tmcRtestrunner's runTests function.
*/
}

@Override
protected StudentFilePolicy getStudentFilePolicy(Path projectPath) {
return null;
return new RStudentFilePolicy(projectPath);
}

@Override
public String getPluginName() {
return null;
return "r";
}

@Override
public Optional<ExerciseDesc> scanExercise(Path path, String exerciseName) {
return null;
ProcessRunner runner = new ProcessRunner(getAvailablePointsCommand(), path);
try {
runner.call();
} catch (Exception e) {
System.out.println(e);
log.error(CANNOT_SCAN_EXERCISE_MESSAGE, e);
}
try {
ImmutableList<TestDesc> testDescs = new RExerciseDescParser(path).parse();
return Optional.of(new ExerciseDesc(exerciseName, testDescs));
} catch (IOException e) {
log.error(CANNOT_PARSE_EXERCISE_DESCRIPTION_MESSAGE, e);
}
return Optional.absent();
}

@Override
Expand Down Expand Up @@ -98,9 +150,16 @@ private String[] getTestCommand() {
}
return ArrayUtils.addAll(rscr, command);
}


private String[] getAvailablePointsCommand() {
String[] rscr = new String[] {"Rscript", "-e"};
String[] command = new String[] {"\"library(tmcRtestrunner);"
+ "getAvailablePoints(\"$PWD\")\""};
return ArrayUtils.addAll(rscr, command);
}

@Override
public void clean(Path path) {

// TO DO
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package fi.helsinki.cs.tmc.langs.r;

import fi.helsinki.cs.tmc.langs.io.ConfigurableStudentFilePolicy;

import java.nio.file.Path;
import java.nio.file.Paths;

public class RStudentFilePolicy extends ConfigurableStudentFilePolicy {
public RStudentFilePolicy(Path configFileParent) {
super(configFileParent);
}

/**
* Returns {@code True} for all files in the <tt>projectRoot/R/</tt> directory and other
* files required for building the project.
*
* <p>Will NOT return {@code True} for any test files. If test file modification are part
* of the exercise, those test files are whitelisted as <tt>ExtraStudentFiles</tt> and the
* decision to include them is made by {@link ConfigurableStudentFilePolicy}.
*/
@Override
public boolean isStudentSourceFile(Path path, Path projectRootPath) {
return path.startsWith(Paths.get("R"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,12 @@ public static void main(String[] args) {

//For now, add the path you want to test here fully,
//for example: pathToGithubFolder/tmc-r/example_projects/example_project1
String exampleProjectLocation = "/tmc-r/example_projects/example_project1";
String exampleProjectLocation = "/example_projects/example_project1";
Path path = Paths.get(exampleProjectLocation);
RunResult runRes = runTests(path);
printTestResult(runRes);
RunResult rr;

/* try {
rr = new RTestResultParser(path).parse();
for (TestResult tr : rr.testResults) {
System.out.println(tr.toString());
}
} catch (IOException e) {
System.out.println("Something wrong: " + e.getMessage());
}*/
}

public static void printTestResult(RunResult rr) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

package fi.helsinki.cs.tmc.langs.r;

import static org.junit.Assert.assertEquals;

import fi.helsinki.cs.tmc.langs.domain.TestDesc;
import fi.helsinki.cs.tmc.langs.utils.TestUtils;

import com.google.common.collect.ImmutableList;

import org.junit.Test;

import java.io.IOException;
import java.nio.file.Path;



public class RExerciseDescParserTest {
private ImmutableList<TestDesc> re;
private Path jsonDir;

public RExerciseDescParserTest() {
jsonDir = TestUtils.getPath(getClass(), "example_json");
try {
re = new RExerciseDescParser(jsonDir).parse();
} catch (IOException e) {
System.out.println("Something wrong: " + e.getMessage());
}
}

@Test
public void testThatParseSeemsToWorkOnExampleJson() {
assertEquals(re.size(),6);
assertEquals(re.get(0).points.size(),2);
assertEquals(re.get(0).name,"Addition works");
assertEquals(re.get(1).points.size(),2);
assertEquals(re.get(1).name,"Multiplication works");
assertEquals(re.get(2).points.size(),1);
assertEquals(re.get(2).name,"Subtraction works");
assertEquals(re.get(3).points.size(),1);
assertEquals(re.get(3).name,"Division works");
assertEquals(re.get(4).points.size(),0);
assertEquals(re.get(4).name, "Test with no points");
assertEquals(re.get(5).points.size(),0);
assertEquals(re.get(5).name, "Dummy test set to fail");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

package fi.helsinki.cs.tmc.langs.r;

import org.junit.Test;

public class RPluginTest {

@Test
public void testGetAvailablePointsCommand(){

}


}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ public void testThatParseSeemsToWorkOnExampleJson() {
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{"Addition works": ["r1.1","r1.2"],
"Multiplication works":["r1.3", "r1.4"],
"Subtraction works":["r1.5"],
"Division works":["r1.6"],
"Test with no points":[],
"Dummy test set to fail":[]
}

Loading