diff --git a/.travis.yml b/.travis.yml index 7da3a6b76..929a1e0fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,6 @@ before_install: - 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 -pl tmc-langs-r diff --git a/tmc-langs-cli/src/main/java/fi/helsinki/cs/tmc/langs/cli/Main.java b/tmc-langs-cli/src/main/java/fi/helsinki/cs/tmc/langs/cli/Main.java index b7e25b4b2..3774a2718 100644 --- a/tmc-langs-cli/src/main/java/fi/helsinki/cs/tmc/langs/cli/Main.java +++ b/tmc-langs-cli/src/main/java/fi/helsinki/cs/tmc/langs/cli/Main.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.nio.file.FileVisitResult; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; @@ -54,6 +55,7 @@ public final class Main { + " Commands:\n" + " checkstyle --exercisePath --outputPath --locale" + " Run checkstyle or similar plugin to project if applicable.\n" + + " compress-project --exercisePath --outputPath\n" + " help" + " Display help information.\n" + " prepare-solutions --exercisePath --outputPath" @@ -118,6 +120,9 @@ private static void run(String command) { case "checkstyle": runCheckCodeStyle(); break; + case "compress-project": + runCompressProject(); + break; case "scan-exercise": runScanExercise(); break; @@ -166,6 +171,17 @@ private static Path getOutputPathFromArgs() { throw new IllegalStateException("No " + OUTPUT_PATH + " provided"); } + private static void runCompressProject() { + Path exercisePath = getExercisePathFromArgs(); + Path outputPath = getOutputPathFromArgs(); + try { + byte[] compressed = executor.compressProject(exercisePath); + Files.write(outputPath, compressed); + } catch (IOException | NoLanguagePluginFoundException e) { + e.printStackTrace(); + } + } + private static void runCheckCodeStyle() { ValidationResult validationResult = null; try { diff --git a/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/AbstractLanguagePlugin.java b/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/AbstractLanguagePlugin.java index e01073001..3c80d6c52 100644 --- a/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/AbstractLanguagePlugin.java +++ b/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/AbstractLanguagePlugin.java @@ -13,6 +13,8 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Stack; @@ -147,9 +149,17 @@ private ImmutableList searchForExercises( } @Override - public ExercisePackagingConfiguration getExercisePackagingConfiguration() { - return new ExercisePackagingConfiguration( - ImmutableList.of("src"), ImmutableList.of("test")); + public ExercisePackagingConfiguration getExercisePackagingConfiguration(Path path) { + Configuration configuration = getConfiguration(path); + List extraStudentFiles = configuration.getExtraStudentFiles(); + List extraStudentStrings = new ArrayList<>(); + for (Path p : extraStudentFiles) { + extraStudentStrings.add(p.toString()); + } + ImmutableList studentFiles = + ImmutableList.builder().add("src").addAll(extraStudentStrings).build(); + ImmutableList src = ImmutableList.of("src"); + return new ExercisePackagingConfiguration(studentFiles, ImmutableList.of("test")); } @Override diff --git a/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/LanguagePlugin.java b/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/LanguagePlugin.java index 6b969c973..5c130be89 100644 --- a/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/LanguagePlugin.java +++ b/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/LanguagePlugin.java @@ -164,7 +164,7 @@ ValidationResult checkCodeStyle(Path path, Locale messageLocale) * Returns configuration which is used to package submission on tmc-server. */ @Beta - public ExercisePackagingConfiguration getExercisePackagingConfiguration(); + public ExercisePackagingConfiguration getExercisePackagingConfiguration(Path path); /** * Runs clean command e.g {@code make clean} for make or {@code mvn clean} for maven. diff --git a/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/domain/Configuration.java b/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/domain/Configuration.java index 7e0c28169..3c1941fff 100644 --- a/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/domain/Configuration.java +++ b/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/domain/Configuration.java @@ -2,24 +2,33 @@ import fi.helsinki.cs.tmc.langs.utils.TmcProjectYmlParser; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Maps; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; +import java.util.List; import java.util.Map; public final class Configuration { + private final TmcProjectYmlParser tmcProjectYmlParser; + private final Path path; private Map options; public static final Path TMC_PROJECT_YML = Paths.get(".tmcproject.yml"); + @VisibleForTesting public Configuration() { + tmcProjectYmlParser = new TmcProjectYmlParser(); options = new HashMap<>(); + path = null; } public Configuration(Path path) { + this.path = path; + tmcProjectYmlParser = new TmcProjectYmlParser(); parseOptions(path); } @@ -40,19 +49,23 @@ public ValueObject get(String key) { return null; } + public List getExtraStudentFiles() { + return tmcProjectYmlParser.parseExtraStudentFiles(path.resolve(TMC_PROJECT_YML)); + } + /** * Parse options from the path. * * @param path Absolute path to configuration, e.g. .tmcproject.yml -file. */ - public void parseOptions(Path path) { + void parseOptions(Path path) { this.options = parseTmcProjectYmlOptions(path); } private Map parseTmcProjectYmlOptions(Path path) { Path configFile = path.resolve(TMC_PROJECT_YML); if (Files.exists(path)) { - return new TmcProjectYmlParser().parseOptions(configFile); + return tmcProjectYmlParser.parseOptions(configFile); } return Maps.newHashMap(); } diff --git a/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/io/ConfigurableStudentFilePolicy.java b/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/io/ConfigurableStudentFilePolicy.java index fcedc4269..25da9a94d 100644 --- a/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/io/ConfigurableStudentFilePolicy.java +++ b/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/io/ConfigurableStudentFilePolicy.java @@ -69,16 +69,18 @@ public boolean isStudentFile(Path path, Path projectRootPath) { projectRootPath); } - /** - * Determines whether a file is an ExtraStudentFile. - */ + /** Determines whether a file is an ExtraStudentFile. */ private boolean isExtraStudentFile(Path path) { if (extraStudentFiles == null) { loadExtraStudentFileList(); } for (Path extraStudentFile : extraStudentFiles) { - if (extraStudentFile.toAbsolutePath().equals(path.toAbsolutePath())) { + Path extraStudentPath = rootPath.resolve(extraStudentFile).toAbsolutePath(); + Path userSuppliedPath = path.toAbsolutePath(); + if (extraStudentPath.equals(userSuppliedPath) + || (userSuppliedPath.startsWith((extraStudentPath)) + && Files.isDirectory(extraStudentPath))) { return true; } } @@ -96,7 +98,7 @@ private void loadExtraStudentFileList() { if (Files.exists(configFile)) { TmcProjectYmlParser parser = new TmcProjectYmlParser(); - extraStudentFiles = parser.parseExtraStudentFiles(configFile, rootPath); + extraStudentFiles = parser.parseExtraStudentFiles(configFile); } } diff --git a/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/utils/TmcProjectYmlParser.java b/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/utils/TmcProjectYmlParser.java index 4554b07f4..d10aa0830 100644 --- a/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/utils/TmcProjectYmlParser.java +++ b/tmc-langs-framework/src/main/java/fi/helsinki/cs/tmc/langs/utils/TmcProjectYmlParser.java @@ -12,6 +12,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -20,18 +21,15 @@ public final class TmcProjectYmlParser implements ConfigurationParser { private static final Logger log = LoggerFactory.getLogger(TmcProjectYmlParser.class); - - private Path rootPath; private List extraStudentFiles; /** * Parses a list of extra student files from a .tmcproject.yml file. */ - public List parseExtraStudentFiles(Path configFilePath, Path projectRootPath) { + public List parseExtraStudentFiles(Path configFilePath) { log.debug("Parsing extra student files from {}", configFilePath); - rootPath = projectRootPath; extraStudentFiles = new ArrayList<>(); Object yamlSpecifications = getYamlSpecs(configFilePath.toAbsolutePath()); @@ -109,12 +107,21 @@ private void addAllIfList(Object files) { private void addIfString(Object value) { if (value instanceof String) { - Path path = this.rootPath.resolve((String) value); + String[] pathParts = ((String) value).split("/"); + Path path = constructPathfromArray(pathParts); extraStudentFiles.add(path); log.trace("Added {} as extra student file", path); } } + private Path constructPathfromArray(String[] parts) { + Path path = Paths.get(parts[0]); + for (int i = 1; i < parts.length; i++) { + path = path.resolve(parts[i]); + } + return path; + } + private String initFileContents(File file) { try { log.trace("Reading config file"); diff --git a/tmc-langs-java/src/main/java/fi/helsinki/cs/tmc/langs/java/maven/MavenPlugin.java b/tmc-langs-java/src/main/java/fi/helsinki/cs/tmc/langs/java/maven/MavenPlugin.java index f954dc2e8..799e64c42 100644 --- a/tmc-langs-java/src/main/java/fi/helsinki/cs/tmc/langs/java/maven/MavenPlugin.java +++ b/tmc-langs-java/src/main/java/fi/helsinki/cs/tmc/langs/java/maven/MavenPlugin.java @@ -109,8 +109,9 @@ protected TestRunFileAndLogs createRunResultFile(Path path) result.getStdErr()); } + // TODO: ADD extra student file support to here too @Override - public ExercisePackagingConfiguration getExercisePackagingConfiguration() { + public ExercisePackagingConfiguration getExercisePackagingConfiguration(Path path) { return new ExercisePackagingConfiguration( ImmutableList.of("src/main"), ImmutableList.of("src/test")); } diff --git a/tmc-langs-python3/src/main/java/fi/helsinki/cs/tmc/langs/python3/Python3Plugin.java b/tmc-langs-python3/src/main/java/fi/helsinki/cs/tmc/langs/python3/Python3Plugin.java index d02ec972a..169cebefd 100644 --- a/tmc-langs-python3/src/main/java/fi/helsinki/cs/tmc/langs/python3/Python3Plugin.java +++ b/tmc-langs-python3/src/main/java/fi/helsinki/cs/tmc/langs/python3/Python3Plugin.java @@ -148,8 +148,9 @@ public void clean(Path path) { // no op } + // TODO: Add extra student files to here too @Override - public ExercisePackagingConfiguration getExercisePackagingConfiguration() { + public ExercisePackagingConfiguration getExercisePackagingConfiguration(Path path) { return new ExercisePackagingConfiguration( ImmutableList.of("src"), ImmutableList.of("test", "tmc")); } diff --git a/tmc-langs-r/getAvailablePoints.sh b/tmc-langs-r/getAvailablePoints.sh new file mode 100644 index 000000000..d3b12b5f7 --- /dev/null +++ b/tmc-langs-r/getAvailablePoints.sh @@ -0,0 +1,4 @@ +#!/bin/sh +#Currently this script needs to be run at project root! + +Rscript -e "library(tmcRtestrunner);get_available_points(\"$PWD\")" diff --git a/tmc-langs-r/runTests.sh b/tmc-langs-r/runTests.sh new file mode 100755 index 000000000..4c98bc059 --- /dev/null +++ b/tmc-langs-r/runTests.sh @@ -0,0 +1,3 @@ +#!/bin/sh +#Currently this script needs to be run at project root! +/usr/bin/Rscript -e "library(tmcRtestrunner);runTestsWithDefault(TRUE)" 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 index e9ea94acb..504ab2c8f 100644 --- 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 @@ -104,7 +104,7 @@ public String getPluginName() { @Override public Optional scanExercise(Path path, String exerciseName) { - ProcessRunner runner = new ProcessRunner(getAvailablePointsCommand(), path); + ProcessRunner runner = new ProcessRunner(this.getAvailablePointsCommand(), path); try { runner.call(); } catch (Exception e) { @@ -143,21 +143,32 @@ public ValidationResult checkCodeStyle(Path path, Locale messageLocale) { return null; } - private String[] getTestCommand() { - String[] rscr = new String[] {"Rscript", "-e"}; + public String[] getTestCommand() { + + String[] rscr; String[] command; if (SystemUtils.IS_OS_WINDOWS) { + rscr = new String[] {"Rscript", "-e"}; command = new String[] {"\"library('tmcRtestrunner');runTestsWithDefault(TRUE)\""}; } else { - command = new String[] {"\"library(tmcRtestrunner);runTests(\"$PWD\", print=TRUE)\""}; + rscr = new String[] {"bash"}; + command = new String[] {Paths.get("").toAbsolutePath().toString() + "/runTests.sh"}; } return ArrayUtils.addAll(rscr, command); } - private String[] getAvailablePointsCommand() { - String[] rscr = new String[] {"Rscript", "-e"}; - String[] command = new String[] {"\"library(tmcRtestrunner);" - + "getAvailablePoints(\"$PWD\")\""}; + public String[] getAvailablePointsCommand() { + String[] rscr; + String[] command; + if (SystemUtils.IS_OS_WINDOWS) { + rscr = new String[] {"Rscript", "-e"}; + command = new String[] {"\"library(tmcRtestrunner);" + + "get_available_points(\"$PWD\")\""}; + } else { + rscr = new String[] {"bash"}; + command = new String[] {Paths.get("").toAbsolutePath().toString() + + "/getAvailablePoints.sh"}; + } return ArrayUtils.addAll(rscr, command); } 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 index cc193ec63..134fc9ebb 100644 --- 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 @@ -2,13 +2,6 @@ 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 fi.helsinki.cs.tmc.langs.utils.TestUtils; - -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; @@ -23,11 +16,12 @@ public class TestMain { 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 = "/example_projects/example_project1"; + /**String exampleProjectLocation = "/path/to/r-project" + + "/example_projects/example_project1"; Path path = Paths.get(exampleProjectLocation); - RunResult runRes = runTests(path); - printTestResult(runRes); - RunResult rr; + RPlugin rplugin = new RPlugin(); + RunResult runRes = rplugin.runTests(path); + printTestResult(runRes);**/ } public static void printTestResult(RunResult rr) { @@ -37,31 +31,4 @@ public static void printTestResult(RunResult rr) { } - 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/RPluginTest.java b/tmc-langs-r/src/test/java/fi/helsinki/cs/tmc/langs/r/RPluginTest.java index f007905ca..0ea7dc3be 100644 --- a/tmc-langs-r/src/test/java/fi/helsinki/cs/tmc/langs/r/RPluginTest.java +++ b/tmc-langs-r/src/test/java/fi/helsinki/cs/tmc/langs/r/RPluginTest.java @@ -7,12 +7,19 @@ import fi.helsinki.cs.tmc.langs.io.StudentFilePolicy; import fi.helsinki.cs.tmc.langs.utils.TestUtils; +import org.apache.commons.lang3.SystemUtils; +import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; + + + public class RPluginTest { private RPlugin plugin; @@ -21,17 +28,66 @@ public class RPluginTest { public void setUp() { plugin = new RPlugin(); } + + @After + public void tearDown() { + Path testDir = TestUtils.getPath(getClass(), "passing"); + File resultsJson = new File(testDir.toAbsolutePath().toString() + "/.results.json"); + resultsJson.delete(); + } @Test - public void testGetAvailablePointsCommand(){ - + public void testGetTestCommand() { + if (SystemUtils.IS_OS_WINDOWS) { + String[] expectedCommand = new String[]{"Rscript", "-e", + "\"library('tmcRtestrunner');runTestsWithDefault(TRUE)\""}; + + Assert.assertArrayEquals(expectedCommand,plugin.getTestCommand()); + } else if (SystemUtils.IS_OS_LINUX) { + String[] expectedCommand = new String[]{"bash", + Paths.get("").toAbsolutePath().toString() + "/runTests.sh"}; + + Assert.assertArrayEquals(expectedCommand,plugin.getTestCommand()); + } } - + + @Test + public void testGetAvailablePointsCommand() { + if (SystemUtils.IS_OS_WINDOWS) { + String[] expectedCommand = new String[]{"Rscript", "-e","\"library('tmcRtestrunner');" + + "get_available_points(\"$PWD\")\""}; + + Assert.assertArrayEquals(expectedCommand,plugin.getAvailablePointsCommand()); + } else if (SystemUtils.IS_OS_LINUX) { + String[] expectedCommand = new String[]{"bash", + Paths.get("").toAbsolutePath().toString() + "/getAvailablePoints.sh"}; + + Assert.assertArrayEquals(expectedCommand,plugin.getAvailablePointsCommand()); + } + } + @Test public void testGetPluginName() { assertEquals("r", plugin.getLanguageName()); } - + /** + * Need to configure .travis.yml for these tests to work + + @Test + public void testScanExercise() { + Path testDir = TestUtils.getPath(getClass(), "passing"); + plugin.scanExercise(testDir, "arithmetics.R"); + } + + @Test + public void testRunTests() { + Path testDir = TestUtils.getPath(getClass(), "passing"); + plugin.runTests(testDir); + File resultsJson = new File(testDir.toAbsolutePath().toString() + "/.results.json"); + + assertTrue(resultsJson.exists()); + } + */ @Test public void excerciseIsCorrectTypeIfItContainsRFolder() { Path testCasesRoot = TestUtils.getPath(getClass(), "recognition_test_cases"); diff --git a/tmc-langs-r/src/test/resources/passing/test/testthat/helperTMC.R b/tmc-langs-r/src/test/resources/passing/test/testthat/helperTMC.R new file mode 100755 index 000000000..51a55d8dc --- /dev/null +++ b/tmc-langs-r/src/test/resources/passing/test/testthat/helperTMC.R @@ -0,0 +1,13 @@ + +#Sets the points for all tests to global environment, wherefrom they can +#be retrieved. +pointsForAllTests <- function(points) { + .GlobalEnv$points_for_all_tests <- points +} + +#The test that wraps around test_that()-method and stores the points +#to global environment. +test <- function(desc, points, code) { + .GlobalEnv$points[[desc]] <- points + test_that(desc, code) +} diff --git a/tmc-langs-util/src/main/java/fi/helsinki/cs/tmc/langs/util/TaskExecutorImpl.java b/tmc-langs-util/src/main/java/fi/helsinki/cs/tmc/langs/util/TaskExecutorImpl.java index b0e14bfff..24ea08283 100644 --- a/tmc-langs-util/src/main/java/fi/helsinki/cs/tmc/langs/util/TaskExecutorImpl.java +++ b/tmc-langs-util/src/main/java/fi/helsinki/cs/tmc/langs/util/TaskExecutorImpl.java @@ -115,7 +115,7 @@ public byte[] compressProject(Path path) throws NoLanguagePluginFoundException, @Override public ExercisePackagingConfiguration getExercisePackagingConfiguration(Path path) throws NoLanguagePluginFoundException { - return getLanguagePlugin(path).getExercisePackagingConfiguration(); + return getLanguagePlugin(path).getExercisePackagingConfiguration(path); } @Override