diff --git a/.old_travis.yml b/.old_travis.yml new file mode 100644 index 000000000..a551fc679 --- /dev/null +++ b/.old_travis.yml @@ -0,0 +1,45 @@ +language: java +sudo: false +cache: + directories: + - "$HOME/.m2" +addons: + apt: + sources: + - deadsnakes + packages: + - valgrind + - check + - python3.4 +env: + global: + - secure: yiW+hXiZEycKFzF19rLlejJX8BTGbncSisBHyE8uulZee5n7UVGXnG1yvt9+6hz0nvMbxL7r1Daql0xxRHXUFB9VYVUKhF0ycHyJO5ze87U51mlIKAL8UnCkmxpkHNdxY45olEdK+mEbZBtot67nenlwGDfxLI7laITkR8IBAvY= + - secure: WVOHmRlzHZhuYqpTVFR2ZjdVDugr/EGEZGhnkWFFh6DE2zfa+na8WSLQdynHjTH1sL2DMg+p4l8siLoKk6Kn9wh+Wah2GP54oKQNIvt+Zl0olTR8TZq/uEo1iU4dqsDbMscId2xK7imdQ9wMKXV0t/bqhjiFR/wxheR96WxvIVs= + - M3_HOME=/usr/local/maven + +before_install: + - curl -L https://static.rust-lang.org/rustup.sh | sh -s -- --channel=stable --yes --prefix=$PWD --disable-sudo + - export PATH=$PATH:$PWD/bin + - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/lib + - mkdir $HOME/bin && ln -s $(which python3.4) $HOME/bin/python3 && export PATH="$HOME/bin:$PATH" + - mvn install -Dmaven.test.skip=true +script: + - mvn clean test + - mvn checkstyle:check + - . ./clitest.sh +after_success: + - mvn clean cobertura:cobertura org.eluder.coveralls:coveralls-maven-plugin:report + - ./deploy.sh +os: + - linux + - osx +jdk: + - oraclejdk8 + - oraclejdk7 + - openjdk7 +matrix: + allow_failures: + - os: osx +notifications: + slack: + secure: elwVbriXdc/RkeP3UfVpSC0N6mbdk60ro7Pu1+2ddktmgQIEHjAVSdzpy2FLGTz/gK2MEXo2XU1rSg3t8GqlJNh1RTASzhyhcCUJTa7UdKe84mcRWDxJffunQKsBpUJv71tXBy1OyL8pcdnw1qkpr5NDyqXvBk67VwepVqy8em4= diff --git a/.travis.yml b/.travis.yml index ff8e0e38d..7da3a6b76 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,6 @@ addons: packages: - valgrind - check - - python3.4 env: global: - secure: yiW+hXiZEycKFzF19rLlejJX8BTGbncSisBHyE8uulZee5n7UVGXnG1yvt9+6hz0nvMbxL7r1Daql0xxRHXUFB9VYVUKhF0ycHyJO5ze87U51mlIKAL8UnCkmxpkHNdxY45olEdK+mEbZBtot67nenlwGDfxLI7laITkR8IBAvY= @@ -18,13 +17,13 @@ env: - M3_HOME=/usr/local/maven before_install: - - curl -L https://static.rust-lang.org/rustup.sh | sh -s -- --channel=stable --yes --prefix=$PWD --disable-sudo - export PATH=$PATH:$PWD/bin - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/lib - mkdir -p $HOME/bin && ln -s $(which python3.4) $HOME/bin/python3 && export PATH="$HOME/bin:$PATH" + - export PATH="$HOME/bin:$PATH" - mvn install -Dmaven.test.skip=true script: - - mvn clean test + - mvn clean test -pl tmc-langs-r - mvn checkstyle:check - . ./clitest.sh after_success: @@ -40,6 +39,4 @@ jdk: matrix: allow_failures: - os: osx -notifications: - slack: - secure: elwVbriXdc/RkeP3UfVpSC0N6mbdk60ro7Pu1+2ddktmgQIEHjAVSdzpy2FLGTz/gK2MEXo2XU1rSg3t8GqlJNh1RTASzhyhcCUJTa7UdKe84mcRWDxJffunQKsBpUJv71tXBy1OyL8pcdnw1qkpr5NDyqXvBk67VwepVqy8em4= + - jdk: oraclejdk7 diff --git a/clitest.sh b/clitest.sh index eba28c5b8..cb422216e 100755 --- a/clitest.sh +++ b/clitest.sh @@ -2,7 +2,7 @@ # The intention is not to really validate the results of the command, # but to make note should any commands fail and other packaging failures. -mvn clean package -Dmaven.test.skip=true +mvn clean package -Dmaven.test.skip=true java -jar tmc-langs-cli/target/tmc-langs-cli-*-SNAPSHOT.jar scan-exercise --exercisePath tmc-langs-java/src/test/resources/maven_exercise --outputPath a cat a diff --git a/old_clitest.sh b/old_clitest.sh new file mode 100755 index 000000000..eba28c5b8 --- /dev/null +++ b/old_clitest.sh @@ -0,0 +1,12 @@ +#!/bin/bash -v +# The intention is not to really validate the results of the command, +# but to make note should any commands fail and other packaging failures. + +mvn clean package -Dmaven.test.skip=true + +java -jar tmc-langs-cli/target/tmc-langs-cli-*-SNAPSHOT.jar scan-exercise --exercisePath tmc-langs-java/src/test/resources/maven_exercise --outputPath a +cat a + +java -jar tmc-langs-cli/target/tmc-langs-cli-*-SNAPSHOT.jar prepare-stubs --exercisePath tmc-langs-java/src/test/resources/ant_arith_funcs/ --outputPath arithfuncs-proj +java -jar tmc-langs-cli/target/tmc-langs-cli-*-SNAPSHOT.jar checkstyle --exercisePath tmc-langs-cli/src/test/resources/arith_funcs --locale=en --outputPath tmp +cat tmp diff --git a/tmc-langs-r/pom.xml b/tmc-langs-r/pom.xml index 2727e5e9a..d84d23f82 100644 --- a/tmc-langs-r/pom.xml +++ b/tmc-langs-r/pom.xml @@ -7,6 +7,20 @@ 0.7.7-SNAPSHOT tmc-langs-r - 1.0-SNAPSHOT + 0.7.7-SNAPSHOT jar + + + + fi.helsinki.cs.tmc + tmc-langs-framework + 0.7.7-SNAPSHOT + + + fi.helsinki.cs.tmc + tmc-langs-util + 0.7.7-SNAPSHOT + + + \ No newline at end of file diff --git a/tmc-langs-r/src/main/java/fi/helsinki/cs/tmc/langs/r/RPlugin.java b/tmc-langs-r/src/main/java/fi/helsinki/cs/tmc/langs/r/RPlugin.java new file mode 100644 index 000000000..fc2bfb89f --- /dev/null +++ b/tmc-langs-r/src/main/java/fi/helsinki/cs/tmc/langs/r/RPlugin.java @@ -0,0 +1,106 @@ +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.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 org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Locale; + +public final class RPlugin extends AbstractLanguagePlugin { + + 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."; + + private static Logger log = LoggerFactory.getLogger(RPlugin.class); + + public RPlugin() { + super( + new ExerciseBuilder(), + new StudentFileAwareSubmissionProcessor(), + new StudentFileAwareZipper(), + new StudentFileAwareUnzipper()); + } + + @Override + public boolean isExerciseTypeCorrect(Path path) { + return false; + } + + @Override + protected StudentFilePolicy getStudentFilePolicy(Path projectPath) { + return null; + } + + @Override + public String getPluginName() { + return null; + } + + @Override + public Optional scanExercise(Path path, String exerciseName) { + return null; + } + + @Override + public RunResult runTests(Path path) { + + ProcessRunner runner = new ProcessRunner(getTestCommand(), path); + try { + runner.call(); + } catch (Exception e) { + log.error(CANNOT_RUN_TESTS_MESSAGE, e); + } + + try { + return new RTestResultParser(path).parse(); + } catch (IOException e) { + log.error(CANNOT_PARSE_TEST_RESULTS_MESSAGE, e); + } + return null; + } + + @Override + public ValidationResult checkCodeStyle(Path path, Locale messageLocale) { + return null; + } + + private String[] getTestCommand() { + String[] rscr = new String[] {"Rscript", "-e"}; + String[] command; + if (SystemUtils.IS_OS_WINDOWS) { + command = new String[] {"\"library('tmcRtestrunner');runTestsWithDefault(TRUE)\""}; + } else { + command = new String[] {"\"library(tmcRtestrunner);runTests(\"$PWD\", print=TRUE)\""}; + } + return ArrayUtils.addAll(rscr, command); + } + + @Override + public void clean(Path path) { + + } +} diff --git a/tmc-langs-r/src/main/java/fi/helsinki/cs/tmc/langs/r/RTestResultParser.java b/tmc-langs-r/src/main/java/fi/helsinki/cs/tmc/langs/r/RTestResultParser.java index d9e76cd72..0ab926fa3 100644 --- a/tmc-langs-r/src/main/java/fi/helsinki/cs/tmc/langs/r/RTestResultParser.java +++ b/tmc-langs-r/src/main/java/fi/helsinki/cs/tmc/langs/r/RTestResultParser.java @@ -1,8 +1,79 @@ package fi.helsinki.cs.tmc.langs.r; +import fi.helsinki.cs.tmc.langs.domain.RunResult; +import fi.helsinki.cs.tmc.langs.domain.TestResult; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +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.HashMap; +import java.util.List; + public class RTestResultParser { - public RTestResultParser() { + Path path; + private ObjectMapper mapper; + + private static Path RESULT_FILE = Paths.get(".results.json"); + + public RTestResultParser(Path path) { + this.path = path; + this.mapper = new ObjectMapper(); + } + + RunResult parse() throws IOException { + List testResults = getTestResults(); + + RunResult.Status status = RunResult.Status.PASSED; + for (TestResult result : testResults) { + if (!result.isSuccessful()) { + status = RunResult.Status.TESTS_FAILED; + } + } + + ImmutableList immutableResults = ImmutableList.copyOf(testResults); + ImmutableMap logs = ImmutableMap.copyOf(new HashMap()); + return new RunResult(status, immutableResults, logs); + } + + private List getTestResults() throws IOException { + byte[] json = Files.readAllBytes(path.resolve(RESULT_FILE)); + + List results = new ArrayList<>(); + + JsonNode tree = mapper.readTree(json); + for (JsonNode node : tree) { + results.add(toTestResult(node)); + } + + return results; } + private TestResult toTestResult(JsonNode node) { + List points = new ArrayList<>(); + for (JsonNode point : node.get("points")) { + points.add(point.asText()); + } + + List backTrace = new ArrayList<>(); + for (JsonNode line : node.get("backtrace")) { + backTrace.add(line.asText()); + } + + boolean passed = node.get("status").asText().equals("pass"); + + return new TestResult( + node.get("name").asText(), + passed, + ImmutableList.copyOf(points), + node.get("message").toString(), + ImmutableList.copyOf(backTrace)); + } } diff --git a/tmc-langs-r/src/main/java/fi/helsinki/cs/tmc/langs/r/TestMain.java b/tmc-langs-r/src/main/java/fi/helsinki/cs/tmc/langs/r/TestMain.java new file mode 100644 index 000000000..249a726c0 --- /dev/null +++ b/tmc-langs-r/src/main/java/fi/helsinki/cs/tmc/langs/r/TestMain.java @@ -0,0 +1,76 @@ +package fi.helsinki.cs.tmc.langs.r; + +import fi.helsinki.cs.tmc.langs.domain.RunResult; +import fi.helsinki.cs.tmc.langs.domain.TestResult; +import fi.helsinki.cs.tmc.langs.utils.ProcessRunner; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; + +import java.io.IOException; + +import java.nio.file.Path; +import java.nio.file.Paths; + +public class TestMain { + + /** + * Just testing. + * + * @param args Nothing. + */ + 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"; + 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) { + for (TestResult tr : rr.testResults) { + System.out.println(tr.toString()); + } + } + + + public static RunResult runTests(Path path) { + + ProcessRunner runner = new ProcessRunner(getTestCommand(), path); + try { + runner.call(); + } catch (Exception e) { + System.out.println("Something wrong: " + e.getMessage()); + } + + try { + return new RTestResultParser(path).parse(); + } catch (IOException e) { + System.out.println("Something wrong: " + e.getMessage()); + } + return null; + } + + private static String[] getTestCommand() { + String[] rscr = new String[]{"Rscript", "-e"}; + String[] command; + if (SystemUtils.IS_OS_WINDOWS) { + command = new String[] {"\"library('tmcRtestrunner');runTestsWithDefault(TRUE)\""}; + } else { + command = new String[] {"\"library(tmcRtestrunner);runTests(\"$PWD\", print=TRUE)\""}; + } + return ArrayUtils.addAll(rscr, command); + } +} diff --git a/tmc-langs-r/src/test/java/fi/helsinki/cs/tmc/langs/r/RTestResultParserTest.java b/tmc-langs-r/src/test/java/fi/helsinki/cs/tmc/langs/r/RTestResultParserTest.java new file mode 100644 index 000000000..f59fd828e --- /dev/null +++ b/tmc-langs-r/src/test/java/fi/helsinki/cs/tmc/langs/r/RTestResultParserTest.java @@ -0,0 +1,46 @@ +package fi.helsinki.cs.tmc.langs.r; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import fi.helsinki.cs.tmc.langs.domain.RunResult; +import fi.helsinki.cs.tmc.langs.domain.TestResult; +import fi.helsinki.cs.tmc.langs.utils.TestUtils; + +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Path; + +public class RTestResultParserTest { + + private RunResult rr; + private Path jsonDir; + + public RTestResultParserTest() { + jsonDir = TestUtils.getPath(getClass(), "example_json"); + try { + rr = new RTestResultParser(jsonDir).parse(); + } catch (IOException e) { + System.out.println("Something wrong: " + e.getMessage()); + } + } + + @Test + public void testThatParseSeemsToWorkOnExampleJson() { + assertEquals(RunResult.Status.TESTS_FAILED, rr.status); + assertEquals(22, rr.testResults.size()); + + for (TestResult tr : rr.testResults) { + if (tr.getName().equals("Addition works")) { + assertTrue(tr.isSuccessful()); + assertEquals(2, tr.points.size()); + assertTrue(tr.points.contains("r1.1")); + assertTrue(tr.points.contains("r1.2")); + } + if (!tr.isSuccessful()) { + assertEquals("Dummy test set to fail", tr.getName()); + } + } + } +} diff --git a/tmc-langs-r/src/test/resources/example_json/.results.json b/tmc-langs-r/src/test/resources/example_json/.results.json new file mode 100644 index 000000000..5d0cb9f6d --- /dev/null +++ b/tmc-langs-r/src/test/resources/example_json/.results.json @@ -0,0 +1,204 @@ +[ + { + "status": "passed", + "name": "Addition works", + "message": "", + "backtrace": "", + "points": [ + "r1.1", + "r1.2" + ] + }, + { + "status": "passed", + "name": "Multiplication works", + "message": "", + "backtrace": "", + "points": [ + "r1.3", + "r1.4" + ] + }, + { + "status": "passed", + "name": "Subtraction works", + "message": "", + "backtrace": "", + "points": [ + "r1.5" + ] + }, + { + "status": "passed", + "name": "Division works", + "message": "", + "backtrace": "", + "points": [ + "r1.6" + ] + }, + { + "status": "passed", + "name": "Test with no points", + "message": "", + "backtrace": "", + "points": [ + + ] + }, + { + "status": "failed", + "name": "Dummy test set to fail", + "message": "Failed with call: expect_true, FALSE\nFALSE isn't true.\nFailed with call: expect_equal, 1, 2\n1 not equal to 2.\n1/1 mismatches\n[1] 1 - 2 == -1\n", + "backtrace": "", + "points": [ + + ] + }, + { + "status": "passed", + "name": "Matrix transpose with [[1,2]] works", + "message": "", + "backtrace": "", + "points": [ + "r2.1" + ] + }, + { + "status": "passed", + "name": "Matrix transpose with [[1,2],[3,4]] works", + "message": "", + "backtrace": "", + "points": [ + "r2.2" + ] + }, + { + "status": "passed", + "name": "Constant string works", + "message": "", + "backtrace": "", + "points": [ + "r3.1" + ] + }, + { + "status": "passed", + "name": "Exercise 1 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.1" + ] + }, + { + "status": "passed", + "name": "Exercise 2 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.2" + ] + }, + { + "status": "passed", + "name": "Exercise 3 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.3", + "r4.4" + ] + }, + { + "status": "passed", + "name": "Exercise 4 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.5" + ] + }, + { + "status": "passed", + "name": "Exercise 5 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.6" + ] + }, + { + "status": "passed", + "name": "Exercise 6 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.7" + ] + }, + { + "status": "passed", + "name": "Exercise 7 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.8" + ] + }, + { + "status": "passed", + "name": "Exercise 8 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.9" + ] + }, + { + "status": "passed", + "name": "Exercise 9 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.10" + ] + }, + { + "status": "passed", + "name": "Exercise 10 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.11" + ] + }, + { + "status": "passed", + "name": "Exercise 11 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.12" + ] + }, + { + "status": "passed", + "name": "Exercise 12 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.13" + ] + }, + { + "status": "passed", + "name": "Exercise 13 is correct", + "message": "", + "backtrace": "", + "points": [ + "r4.14" + ] + } +] +