diff --git a/build.gradle b/build.gradle index 29da90bf3674..cdad264bac58 100644 --- a/build.gradle +++ b/build.gradle @@ -246,14 +246,15 @@ dependencies { implementation "org.gitlab4j:gitlab4j-api:6.0.0-rc.5" implementation "de.jplag:jplag:${jplag_version}" - implementation "de.jplag:java:${jplag_version}" - implementation "de.jplag:kotlin:${jplag_version}" + implementation "de.jplag:c:${jplag_version}" - implementation "de.jplag:swift:${jplag_version}" implementation "de.jplag:java:${jplag_version}" + implementation "de.jplag:javascript:${jplag_version}" + implementation "de.jplag:kotlin:${jplag_version}" implementation "de.jplag:python-3:${jplag_version}" + implementation "de.jplag:rlang:${jplag_version}" implementation "de.jplag:rust:${jplag_version}" - implementation "de.jplag:javascript:${jplag_version}" + implementation "de.jplag:swift:${jplag_version}" implementation "de.jplag:text:${jplag_version}" // those are transitive dependencies of JPlag Text --> Stanford NLP diff --git a/docs/user/exercises/programming-exercise-features.inc b/docs/user/exercises/programming-exercise-features.inc index 7bccf1596315..660e2bd4bf02 100644 --- a/docs/user/exercises/programming-exercise-features.inc +++ b/docs/user/exercises/programming-exercise-features.inc @@ -37,6 +37,8 @@ Instructors can still use those templates to generate programming exercises and +----------------------+----------+---------+ | JavaScript | yes | yes | +----------------------+----------+---------+ + | R | yes | yes | + +----------------------+----------+---------+ - Not all ``templates`` support the same feature set and supported features can also change depending on the continuous integration system setup. Depending on the feature set, some options might not be available during the creation of the programming exercise. @@ -71,6 +73,8 @@ Instructors can still use those templates to generate programming exercises and +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ | JavaScript | no | no | yes | no | n/a | no | no | L: yes, J: no | +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ + | R | no | no | yes | no | n/a | no | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - *Sequential Test Runs*: ``Artemis`` can generate a build plan which first executes structural and then behavioral tests. This feature can help students to better concentrate on the immediate challenge at hand. - *Static Code Analysis*: ``Artemis`` can generate a build plan which additionally executes static code analysis tools. diff --git a/src/main/java/de/tum/cit/aet/artemis/plagiarism/service/ProgrammingPlagiarismDetectionService.java b/src/main/java/de/tum/cit/aet/artemis/plagiarism/service/ProgrammingPlagiarismDetectionService.java index 9fe9b1cc0f8c..ea6ebfdf3b81 100644 --- a/src/main/java/de/tum/cit/aet/artemis/plagiarism/service/ProgrammingPlagiarismDetectionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/plagiarism/service/ProgrammingPlagiarismDetectionService.java @@ -37,6 +37,7 @@ import de.jplag.options.JPlagOptions; import de.jplag.python3.PythonLanguage; import de.jplag.reporting.reportobject.ReportObjectFactory; +import de.jplag.rlang.RLanguage; import de.jplag.rust.RustLanguage; import de.jplag.swift.SwiftLanguage; import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; @@ -310,14 +311,15 @@ public void deleteTempLocalRepository(Repository repository) { private Language getJPlagProgrammingLanguage(ProgrammingExercise programmingExercise) { return switch (programmingExercise.getProgrammingLanguage()) { - case JAVA -> new JavaLanguage(); case C -> new CLanguage(); - case PYTHON -> new PythonLanguage(); - case SWIFT -> new SwiftLanguage(); + case JAVA -> new JavaLanguage(); + case JAVASCRIPT -> new JavaScriptLanguage(); case KOTLIN -> new KotlinLanguage(); + case PYTHON -> new PythonLanguage(); + case R -> new RLanguage(); case RUST -> new RustLanguage(); - case JAVASCRIPT -> new JavaScriptLanguage(); - case EMPTY, PHP, DART, HASKELL, ASSEMBLER, OCAML, C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, GO, MATLAB, BASH, VHDL, RUBY, POWERSHELL, ADA -> + case SWIFT -> new SwiftLanguage(); + case EMPTY, PHP, DART, HASKELL, ASSEMBLER, OCAML, C_SHARP, C_PLUS_PLUS, SQL, TYPESCRIPT, GO, MATLAB, BASH, VHDL, RUBY, POWERSHELL, ADA -> throw new BadRequestAlertException("Programming language " + programmingExercise.getProgrammingLanguage() + " not supported for plagiarism check.", "ProgrammingExercise", "notSupported"); }; diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingLanguage.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingLanguage.java index 4206bfe15dbc..781ad04f98c6 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingLanguage.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingLanguage.java @@ -38,18 +38,19 @@ public enum ProgrammingLanguage { PHP("php"); private static final Set ENABLED_LANGUAGES = Set.of( - EMPTY, - JAVA, - PYTHON, + ASSEMBLER, C, HASKELL, + JAVA, + JAVASCRIPT, KOTLIN, - VHDL, - ASSEMBLER, - SWIFT, OCAML, + PYTHON, + R, RUST, - JAVASCRIPT + SWIFT, + VHDL, + EMPTY ); // @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/TemplateUpgradePolicyService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/TemplateUpgradePolicyService.java index 16285e6a0695..4c73046b1ab0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/TemplateUpgradePolicyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/TemplateUpgradePolicyService.java @@ -32,8 +32,8 @@ public TemplateUpgradePolicyService(JavaTemplateUpgradeService javaRepositoryUpg public TemplateUpgradeService getUpgradeService(ProgrammingLanguage programmingLanguage) { return switch (programmingLanguage) { case JAVA -> javaRepositoryUpgradeService; - case KOTLIN, PYTHON, C, HASKELL, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT -> defaultRepositoryUpgradeService; - case C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case KOTLIN, PYTHON, C, HASKELL, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT, R -> defaultRepositoryUpgradeService; + case C_SHARP, C_PLUS_PLUS, SQL, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + programmingLanguage); }; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationService.java index b4f67794c073..b9050501c67a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationService.java @@ -219,8 +219,8 @@ enum RepositoryCheckoutPath implements CustomizableCheckoutPath { @Override public String forProgrammingLanguage(ProgrammingLanguage language) { return switch (language) { - case JAVA, PYTHON, C, HASKELL, KOTLIN, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT -> "assignment"; - case C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case JAVA, PYTHON, C, HASKELL, KOTLIN, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT, R -> "assignment"; + case C_SHARP, C_PLUS_PLUS, SQL, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + language); }; } @@ -230,9 +230,9 @@ public String forProgrammingLanguage(ProgrammingLanguage language) { @Override public String forProgrammingLanguage(ProgrammingLanguage language) { return switch (language) { - case JAVA, PYTHON, HASKELL, KOTLIN, SWIFT, EMPTY, RUST, JAVASCRIPT -> ""; + case JAVA, PYTHON, HASKELL, KOTLIN, SWIFT, EMPTY, RUST, JAVASCRIPT, R -> ""; case C, VHDL, ASSEMBLER, OCAML -> "tests"; - case C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case C_SHARP, C_PLUS_PLUS, SQL, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + language); }; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIProgrammingLanguageFeatureService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIProgrammingLanguageFeatureService.java index a92bcdd26cb5..0c71114e13bb 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIProgrammingLanguageFeatureService.java @@ -25,7 +25,7 @@ public class GitLabCIProgrammingLanguageFeatureService extends ProgrammingLangua public GitLabCIProgrammingLanguageFeatureService() { programmingLanguageFeatures.put(EMPTY, new ProgrammingLanguageFeature(EMPTY, false, false, false, false, false, List.of(), false, false)); programmingLanguageFeatures.put(JAVA, new ProgrammingLanguageFeature(JAVA, false, false, false, true, false, List.of(PLAIN_MAVEN, MAVEN_MAVEN), false, false)); - programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), false, false)); programmingLanguageFeatures.put(JAVASCRIPT, new ProgrammingLanguageFeature(JAVASCRIPT, false, false, true, false, false, List.of(), false, false)); + programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), false, false)); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java index 38893ea41093..45a473da9148 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java @@ -7,6 +7,7 @@ import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.JAVASCRIPT; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.KOTLIN; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.PYTHON; +import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.R; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.RUST; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.SWIFT; import static de.tum.cit.aet.artemis.programming.domain.ProjectType.FACT; @@ -33,15 +34,16 @@ public class JenkinsProgrammingLanguageFeatureService extends ProgrammingLanguag public JenkinsProgrammingLanguageFeatureService() { // Must be extended once a new programming language is added programmingLanguageFeatures.put(EMPTY, new ProgrammingLanguageFeature(EMPTY, false, false, false, false, false, List.of(), false, false)); + programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, false, true, false, false, List.of(FACT, GCC), false, false)); + programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, false, false, false, false, true, List.of(), false, false)); programmingLanguageFeatures.put(JAVA, new ProgrammingLanguageFeature(JAVA, true, true, true, true, false, List.of(PLAIN_GRADLE, GRADLE_GRADLE, PLAIN_MAVEN, MAVEN_MAVEN, MAVEN_BLACKBOX), true, false)); + programmingLanguageFeatures.put(JAVASCRIPT, new ProgrammingLanguageFeature(JAVASCRIPT, false, false, true, false, false, List.of(), false, false)); programmingLanguageFeatures.put(KOTLIN, new ProgrammingLanguageFeature(KOTLIN, true, false, true, true, false, List.of(), true, false)); programmingLanguageFeatures.put(PYTHON, new ProgrammingLanguageFeature(PYTHON, false, false, true, false, false, List.of(), false, false)); + programmingLanguageFeatures.put(R, new ProgrammingLanguageFeature(R, false, false, true, false, false, List.of(), false, false)); + programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), false, false)); // Jenkins is not supporting XCODE at the moment programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, true, true, true, false, List.of(PLAIN), false, false)); - programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, false, true, false, false, List.of(FACT, GCC), false, false)); - programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, false, false, false, false, true, List.of(), false, false)); - programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), false, false)); - programmingLanguageFeatures.put(JAVASCRIPT, new ProgrammingLanguageFeature(JAVASCRIPT, false, false, true, false, false, List.of(), false, false)); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanService.java index 6e904910ca57..f900cc0f6dd1 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanService.java @@ -184,8 +184,8 @@ private JenkinsXmlConfigBuilder builderFor(ProgrammingLanguage programmingLangua throw new UnsupportedOperationException("Xcode templates are not available for Jenkins."); } return switch (programmingLanguage) { - case JAVA, KOTLIN, PYTHON, C, HASKELL, SWIFT, EMPTY, RUST, JAVASCRIPT -> jenkinsBuildPlanCreator; - case VHDL, ASSEMBLER, OCAML, C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case JAVA, KOTLIN, PYTHON, C, HASKELL, SWIFT, EMPTY, RUST, JAVASCRIPT, R -> jenkinsBuildPlanCreator; + case VHDL, ASSEMBLER, OCAML, C_SHARP, C_PLUS_PLUS, SQL, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException(programmingLanguage + " templates are not available for Jenkins."); }; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java index 525170cca334..bc8292d407bb 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java @@ -10,6 +10,7 @@ import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.KOTLIN; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.OCAML; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.PYTHON; +import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.R; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.RUST; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.SWIFT; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.VHDL; @@ -39,17 +40,18 @@ public class LocalCIProgrammingLanguageFeatureService extends ProgrammingLanguag public LocalCIProgrammingLanguageFeatureService() { // Must be extended once a new programming language is added programmingLanguageFeatures.put(EMPTY, new ProgrammingLanguageFeature(EMPTY, false, false, false, false, false, List.of(), false, true)); + programmingLanguageFeatures.put(ASSEMBLER, new ProgrammingLanguageFeature(ASSEMBLER, false, false, false, false, false, List.of(), false, true)); + programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, true, true, false, false, List.of(FACT, GCC), false, true)); + programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, true, false, false, false, true, List.of(), false, true)); programmingLanguageFeatures.put(JAVA, new ProgrammingLanguageFeature(JAVA, true, true, true, true, false, List.of(PLAIN_GRADLE, GRADLE_GRADLE, PLAIN_MAVEN, MAVEN_MAVEN), false, true)); - programmingLanguageFeatures.put(PYTHON, new ProgrammingLanguageFeature(PYTHON, false, false, true, false, false, List.of(), false, true)); - programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, true, true, false, false, List.of(FACT, GCC), false, true)); - programmingLanguageFeatures.put(ASSEMBLER, new ProgrammingLanguageFeature(ASSEMBLER, false, false, false, false, false, List.of(), false, true)); + programmingLanguageFeatures.put(JAVASCRIPT, new ProgrammingLanguageFeature(JAVASCRIPT, false, false, true, false, false, List.of(), false, true)); programmingLanguageFeatures.put(KOTLIN, new ProgrammingLanguageFeature(KOTLIN, false, false, true, true, false, List.of(), false, true)); - programmingLanguageFeatures.put(VHDL, new ProgrammingLanguageFeature(VHDL, false, false, false, false, false, List.of(), false, true)); - programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, true, false, false, false, true, List.of(), false, true)); programmingLanguageFeatures.put(OCAML, new ProgrammingLanguageFeature(OCAML, false, false, false, false, true, List.of(), false, true)); - programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, false, true, true, false, List.of(PLAIN), false, true)); + programmingLanguageFeatures.put(PYTHON, new ProgrammingLanguageFeature(PYTHON, false, false, true, false, false, List.of(), false, true)); + programmingLanguageFeatures.put(R, new ProgrammingLanguageFeature(R, false, false, true, false, false, List.of(), false, true)); programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), false, true)); - programmingLanguageFeatures.put(JAVASCRIPT, new ProgrammingLanguageFeature(JAVASCRIPT, false, false, true, false, false, List.of(), false, true)); + programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, false, true, true, false, List.of(PLAIN), false, true)); + programmingLanguageFeatures.put(VHDL, new ProgrammingLanguageFeature(VHDL, false, false, false, false, false, List.of(), false, true)); } } diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml index 924d087ec8f2..3924e2d804f9 100644 --- a/src/main/resources/config/application.yml +++ b/src/main/resources/config/application.yml @@ -91,6 +91,8 @@ artemis: default: "ghcr.io/ls1intum/artemis-rust-docker:v0.9.70" javascript: default: "ghcr.io/ls1intum/artemis-javascript-docker:v1.0.0" + r: + default: "ghcr.io/ls1intum/artemis-r-docker:v1.0.0" management: endpoints: diff --git a/src/main/resources/templates/aeolus/r/default.sh b/src/main/resources/templates/aeolus/r/default.sh new file mode 100644 index 000000000000..1d0b32e87105 --- /dev/null +++ b/src/main/resources/templates/aeolus/r/default.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -e +export AEOLUS_INITIAL_DIRECTORY=${PWD} +install () { + echo '⚙️ executing install' + R CMD INSTALL assignment +} + +run_all_tests () { + echo '⚙️ executing run_all_tests' + Rscript -e 'library("testthat"); options(testthat.output_file = "junit.xml"); test_local(".", reporter = "junit")' +} + +main () { + if [[ "${1}" == "aeolus_sourcing" ]]; then + return 0 # just source to use the methods in the subshell, no execution + fi + local _script_name + _script_name=${BASH_SOURCE[0]:-$0} + cd "${AEOLUS_INITIAL_DIRECTORY}" + bash -c "source ${_script_name} aeolus_sourcing; install" + cd "${AEOLUS_INITIAL_DIRECTORY}" + bash -c "source ${_script_name} aeolus_sourcing; run_all_tests" +} + +main "${@}" diff --git a/src/main/resources/templates/aeolus/r/default.yaml b/src/main/resources/templates/aeolus/r/default.yaml new file mode 100644 index 000000000000..a41d23c6f012 --- /dev/null +++ b/src/main/resources/templates/aeolus/r/default.yaml @@ -0,0 +1,14 @@ +api: v0.0.1 +metadata: + name: R + id: r + description: Test package using testthat +actions: + - name: install + script: R CMD INSTALL assignment + - name: run_all_tests + script: Rscript -e 'library("testthat"); options(testthat.output_file = "junit.xml"); test_local(".", reporter = "junit")' + results: + - name: junit + path: tests/testthat/junit.xml + type: junit diff --git a/src/main/resources/templates/jenkins/r/regularRuns/pipeline.groovy b/src/main/resources/templates/jenkins/r/regularRuns/pipeline.groovy new file mode 100644 index 000000000000..9a2ec97b5843 --- /dev/null +++ b/src/main/resources/templates/jenkins/r/regularRuns/pipeline.groovy @@ -0,0 +1,59 @@ +/* + * This file configures the actual build steps for the automatic grading. + * + * !!! + * For regular exercises, there is no need to make changes to this file. + * Only this base configuration is actively supported by the Artemis maintainers + * and/or your Artemis instance administrators. + * !!! + */ + +dockerImage = '#dockerImage' +dockerFlags = '#dockerArgs' + +/** + * Main function called by Jenkins. + */ +void testRunner() { + docker.image(dockerImage).inside(dockerFlags) { c -> + runTestSteps() + } +} + +private void runTestSteps() { + test() +} + +/** + * Run unit tests + */ +private void test() { + stage('Test') { + sh ''' + R CMD INSTALL assignment + Rscript -e 'library("testthat"); options(testthat.output_file = "junit.xml"); test_local(".", reporter = "junit")' + ''' + } +} + +/** + * Script of the post build tasks aggregating all JUnit files in $WORKSPACE/results. + * + * Called by Jenkins. + */ +void postBuildTasks() { + sh ''' + rm -rf results + mkdir results + if [ -e tests/testthat/junit.xml ] + then + sed -i 's/]*>//g ; s/<\\/testsuites>/<\\/testsuite>/g' tests/testthat/junit.xml + fi + cp tests/testthat/junit.xml $WORKSPACE/results/ || true + sed -i 's/[^[:print:]\t]/�/g' $WORKSPACE/results/*.xml || true + ''' +} + +// very important, do not remove +// required so that Jenkins finds the methods defined in this script +return this diff --git a/src/main/resources/templates/r/exercise/DESCRIPTION b/src/main/resources/templates/r/exercise/DESCRIPTION new file mode 100644 index 000000000000..2933cb767621 --- /dev/null +++ b/src/main/resources/templates/r/exercise/DESCRIPTION @@ -0,0 +1,7 @@ +Package: assignment +Title: Artemis R Student Assignment +Version: 0.0.0.9000 +Author: Artemis +Description: This is an assignment to be solved by students. +License: MIT +Encoding: UTF-8 diff --git a/src/main/resources/templates/r/exercise/NAMESPACE b/src/main/resources/templates/r/exercise/NAMESPACE new file mode 100644 index 000000000000..9c9f9ac2d917 --- /dev/null +++ b/src/main/resources/templates/r/exercise/NAMESPACE @@ -0,0 +1 @@ +exportPattern("^[^\\.]") diff --git a/src/main/resources/templates/r/exercise/R/convert.R b/src/main/resources/templates/r/exercise/R/convert.R new file mode 100644 index 000000000000..28e787cf2967 --- /dev/null +++ b/src/main/resources/templates/r/exercise/R/convert.R @@ -0,0 +1,3 @@ +matrix_to_column_list <- function(mat) { + # TODO: implement +} diff --git a/src/main/resources/templates/r/readme b/src/main/resources/templates/r/readme new file mode 100644 index 000000000000..73377139d293 --- /dev/null +++ b/src/main/resources/templates/r/readme @@ -0,0 +1,6 @@ +# Matrix Columns + +Write a function `matrix_to_column_list` in R that takes a matrix of any shape and converts it into a list of +column-vectors. Each element of the list should represent a column of the matrix. + +1. [task][Convert to column-vectors](converts_3x3_matrix_to_vectors,converts_4x2_matrix_to_vectors,converts_1x5_matrix_to_scalars,converts_5x1_matrix_to_vector) diff --git a/src/main/resources/templates/r/solution/DESCRIPTION b/src/main/resources/templates/r/solution/DESCRIPTION new file mode 100644 index 000000000000..2933cb767621 --- /dev/null +++ b/src/main/resources/templates/r/solution/DESCRIPTION @@ -0,0 +1,7 @@ +Package: assignment +Title: Artemis R Student Assignment +Version: 0.0.0.9000 +Author: Artemis +Description: This is an assignment to be solved by students. +License: MIT +Encoding: UTF-8 diff --git a/src/main/resources/templates/r/solution/NAMESPACE b/src/main/resources/templates/r/solution/NAMESPACE new file mode 100644 index 000000000000..9c9f9ac2d917 --- /dev/null +++ b/src/main/resources/templates/r/solution/NAMESPACE @@ -0,0 +1 @@ +exportPattern("^[^\\.]") diff --git a/src/main/resources/templates/r/solution/R/convert.R b/src/main/resources/templates/r/solution/R/convert.R new file mode 100644 index 000000000000..7d701772ab7b --- /dev/null +++ b/src/main/resources/templates/r/solution/R/convert.R @@ -0,0 +1,17 @@ +matrix_to_column_list <- function(mat) { + if (!is.matrix(mat)) { + stop("Input must be a matrix") + } + + n_cols <- ncol(mat) + + # Initialize an empty list to store column-vectors + column_list <- vector("list", length = n_cols) + + # Loop through each column and store it in the list + for (i in 1:n_cols) { + column_list[[i]] <- mat[, i] + } + + return(column_list) +} diff --git a/src/main/resources/templates/r/test/DESCRIPTION b/src/main/resources/templates/r/test/DESCRIPTION new file mode 100644 index 000000000000..e19a2b735419 --- /dev/null +++ b/src/main/resources/templates/r/test/DESCRIPTION @@ -0,0 +1,14 @@ +Package: test +Title: Artemis R Tests +Version: 0.0.0.9000 +Author: Artemis +Description: This package tests the student assignment. +License: MIT +Encoding: UTF-8 +Imports: + assignment +Remotes: + local::./assignment +Suggests: + testthat (>= 3.0.0) +Config/testthat/edition: 3 diff --git a/src/main/resources/templates/r/test/tests/testthat.R b/src/main/resources/templates/r/test/tests/testthat.R new file mode 100644 index 000000000000..388438828173 --- /dev/null +++ b/src/main/resources/templates/r/test/tests/testthat.R @@ -0,0 +1,12 @@ +# This file is part of the standard setup for testthat. +# It is recommended that you do not modify it. +# +# Where should you do additional test configuration? +# Learn more about the roles of various files in: +# * https://r-pkgs.org/testing-design.html#sec-tests-files-overview +# * https://testthat.r-lib.org/articles/special-files.html + +library(testthat) +library(tests) + +test_check("tests") diff --git a/src/main/resources/templates/r/test/tests/testthat/test-convert.R b/src/main/resources/templates/r/test/tests/testthat/test-convert.R new file mode 100644 index 000000000000..a84a0e879711 --- /dev/null +++ b/src/main/resources/templates/r/test/tests/testthat/test-convert.R @@ -0,0 +1,47 @@ +test_that("converts_3x3_matrix_to_vectors", { + mat <- matrix(c(5, 8, 11, 6, 9, 12, 7, 10, 13), nrow = 3, ncol = 3) + + result <- assignment::matrix_to_column_list(mat) + + # Make sure to only use exactly one "expect_" function per test + expect_equal(result, list( + c(5, 8, 11), + c(6, 9, 12), + c(7, 10, 13) + )) +}) + +test_that("converts_4x2_matrix_to_vectors", { + mat <- matrix(c(13, 13, 5, 18, 11, 4, 7, 10), nrow = 4, ncol = 2) + + result <- assignment::matrix_to_column_list(mat) + + expect_equal(result, list( + c(13, 13, 5, 18), + c(11, 4, 7, 10) + )) +}) + +test_that("converts_1x5_matrix_to_scalars", { + mat <- matrix(c(16, 10, 15, 8, 7), nrow = 1, ncol = 5) + + result <- assignment::matrix_to_column_list(mat) + + expect_equal(result, list( + 16, + 10, + 15, + 8, + 7 + )) +}) + +test_that("converts_5x1_matrix_to_vector", { + mat <- matrix(c(14, 9, 1, 3, 4), nrow = 5, ncol = 1) + + result <- assignment::matrix_to_column_list(mat) + + expect_equal(result, list( + c(14, 9, 1, 3, 4) + )) +}) diff --git a/src/main/webapp/app/entities/programming/programming-exercise.model.ts b/src/main/webapp/app/entities/programming/programming-exercise.model.ts index ef2d95985068..17d04e971160 100644 --- a/src/main/webapp/app/entities/programming/programming-exercise.model.ts +++ b/src/main/webapp/app/entities/programming/programming-exercise.model.ts @@ -13,18 +13,19 @@ import { SubmissionPolicy } from 'app/entities/submission-policy.model'; import dayjs from 'dayjs/esm'; export enum ProgrammingLanguage { - JAVA = 'JAVA', - PYTHON = 'PYTHON', + EMPTY = 'EMPTY', + ASSEMBLER = 'ASSEMBLER', C = 'C', HASKELL = 'HASKELL', + JAVA = 'JAVA', + JAVASCRIPT = 'JAVASCRIPT', KOTLIN = 'KOTLIN', - VHDL = 'VHDL', - ASSEMBLER = 'ASSEMBLER', - SWIFT = 'SWIFT', OCAML = 'OCAML', - EMPTY = 'EMPTY', + PYTHON = 'PYTHON', + R = 'R', RUST = 'RUST', - JAVASCRIPT = 'JAVASCRIPT', + SWIFT = 'SWIFT', + VHDL = 'VHDL', } export enum ProjectType { diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/supported-file-extensions.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/supported-file-extensions.ts index 73ec34bebb00..89664e7f5963 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/supported-file-extensions.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/supported-file-extensions.ts @@ -1,5 +1,6 @@ export const supportedTextFileExtensions = [ 'Makefile', + 'R', 'Rakefile', 'ada', 'adb', diff --git a/src/test/resources/config/application.yml b/src/test/resources/config/application.yml index f48155253d32..ee94c5be7573 100644 --- a/src/test/resources/config/application.yml +++ b/src/test/resources/config/application.yml @@ -70,6 +70,8 @@ artemis: default: "~~invalid~~" javascript: default: "~~invalid~~" + r: + default: "~~invalid~~" spring: application: