diff --git a/.github/issue-labeler.yml b/.github/issue-labeler.yml index 34ca1e28321c..8fc424b1e785 100644 --- a/.github/issue-labeler.yml +++ b/.github/issue-labeler.yml @@ -111,7 +111,6 @@ programming: - build plan - code hint - \b(? -
LocalCIBuildJobQueueItem
id: String
name: String
buildAgentAddress: String
participationId: long
courseId: long
exerciseId: long
retryCount: int
priority: int
status: BuildJobResult
RepositoryInfo
repositoryName: String
repositoryType: RepositoryType
triggeredByPushTo: RepositoryType
assignmentRepositoryUri: String
testRepositoryUri: String
solutionRepositoryUri: String
auxiliaryRepositoryUris: String []
auxiliaryRepositoryCheckoutDirectories: String []
<<Enumeration>>
BuildJobResult
SUCCESSFUL
FAILED
CANCELLED
<<Enumeration>>
RepositoryType
TEMPLATE
SOLUTION
TESTS
AUXILIARY
USER
JobTimingInfo
submissionDate: ZonedDateTime
buildStartDate: ZonedDateTime
buildCompletionDate: ZonedDateTime
1
BuildConfig
buildScript: String
dockerImage: String
commitHash: String
commitHash: String
branch: String
programmingLanguage: ProgrammingLanguage
projectType: ProjectType
scaEnabled: boolean
sequentialTestRunsEnabled: boolean
testwiseCoverageEnabled: boolean
resultPaths: String []
1
1
\ No newline at end of file +
LocalCIBuildJobQueueItem
id: String
name: String
buildAgentAddress: String
participationId: long
courseId: long
exerciseId: long
retryCount: int
priority: int
status: BuildJobResult
RepositoryInfo
repositoryName: String
repositoryType: RepositoryType
triggeredByPushTo: RepositoryType
assignmentRepositoryUri: String
testRepositoryUri: String
solutionRepositoryUri: String
auxiliaryRepositoryUris: String []
auxiliaryRepositoryCheckoutDirectories: String []
<<Enumeration>>
BuildJobResult
SUCCESSFUL
FAILED
CANCELLED
<<Enumeration>>
RepositoryType
TEMPLATE
SOLUTION
TESTS
AUXILIARY
USER
JobTimingInfo
submissionDate: ZonedDateTime
buildStartDate: ZonedDateTime
buildCompletionDate: ZonedDateTime
1
BuildConfig
buildScript: String
dockerImage: String
commitHash: String
commitHash: String
branch: String
programmingLanguage: ProgrammingLanguage
projectType: ProjectType
scaEnabled: boolean
sequentialTestRunsEnabled: boolean
resultPaths: String []
1
1
diff --git a/docs/user/exercises/programming-exercise-features.inc b/docs/user/exercises/programming-exercise-features.inc index 18434aaed70f..1b43ab3cae7b 100644 --- a/docs/user/exercises/programming-exercise-features.inc +++ b/docs/user/exercises/programming-exercise-features.inc @@ -54,43 +54,43 @@ Instructors can still use those templates to generate programming exercises and In case a feature has different support for different continuous integration systems, the table shows the differences between Local CI **(L)** and Jenkins **(J)**. (Note that Gitlab CI is experimental and therefore has a limited set of features which are not mentioned here to keep the overview simpler.) - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | Programming Language | Sequential Test Runs | Static Code Analysis | Plagiarism Check | Package Name | Project Type | Solution Repository Checkout | Testwise Coverage Analysis | Auxiliary repositories | - +======================+======================+======================+=====================+==============+==========================================+==============================+============================+========================+ - | Java | yes | yes | yes | yes | Gradle, Maven, J: `DejaGnu`_ | no | J: yes; L: no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | Python | L: yes; J: no | no | yes | no | n/a | no | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | C | no | L: yes; J: no | yes | no | FACT, GCC | no | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | C (FACT framework) | no | L: yes; J: no | yes | no | n/a | no | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | Haskell | L: yes; J: no | no | no | no | n/a | L: yes; J: no | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | Kotlin | yes | no | yes | yes | n/a | no | J: yes; L: no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | VHDL | no | no | no | no | n/a | no | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | Assembler | no | no | no | no | n/a | no | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | Swift | no | yes | yes | yes | Plain (Xcode: not supported) | no | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | OCaml | no | no | no | no | n/a | yes | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | Rust | no | no | yes | no | n/a | no | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | 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 | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | C++ | no | no | yes | no | n/a | no | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | TypeScript | no | no | yes | no | n/a | no | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | C# | no | no | yes | no | n/a | no | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - | Go | no | no | yes | yes | n/a | no | no | L: yes, J: no | - +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | Programming Language | Sequential Test Runs | Static Code Analysis | Plagiarism Check | Package Name | Project Type | Solution Repository Checkout | Auxiliary repositories | + +======================+======================+======================+=====================+==============+==========================================+==============================+========================+ + | Java | yes | yes | yes | yes | Gradle, Maven, J: `DejaGnu`_ | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | Python | L: yes; J: no | no | yes | no | n/a | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | C | no | L: yes; J: no | yes | no | FACT, GCC | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | C (FACT framework) | no | L: yes; J: no | yes | no | n/a | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | Haskell | L: yes; J: no | no | no | no | n/a | L: yes; J: no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | Kotlin | yes | no | yes | yes | n/a | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | VHDL | no | no | no | no | n/a | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | Assembler | no | no | no | no | n/a | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | Swift | no | yes | yes | yes | Plain (Xcode: not supported) | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | OCaml | no | no | no | no | n/a | yes | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | Rust | no | no | yes | no | n/a | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | JavaScript | no | no | yes | no | n/a | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | R | no | no | yes | no | n/a | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | C++ | no | no | yes | no | n/a | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | TypeScript | no | no | yes | no | n/a | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | C# | no | no | yes | no | n/a | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | Go | no | no | yes | yes | n/a | 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. @@ -98,8 +98,6 @@ Instructors can still use those templates to generate programming exercises and - *Plagiarism Checks*: ``Artemis`` is able to automatically calculate the similarity between student submissions. A side-by-side view of similar submissions is available to confirm the plagiarism suspicion. - *Package Name*: A package name has to be provided - *Solution Repository Checkout*: Instructors are able to compare a student submission against a sample solution in the solution repository - - *Testwise Coverage Analysis*: ``Artemis`` can generate a build plan which additionally executes a testwise coverage analysis. - ``Artemis`` aggregates the recorded data into different metrics. This feature allows instructors to check which code in the solution submission is how often executed by the test cases. .. note:: Only some ``templates`` for ``LocalCI`` support ``Sequential Test Runs`` at the moment. @@ -107,9 +105,6 @@ Instructors can still use those templates to generate programming exercises and .. note:: Static Code Analysis for ``C`` exercises is only supported for ``LocalCI`` at the moment. -.. note:: - Testwise Coverage Analysis is currently not supported, but will be again supported in future versions. - .. note:: Instructors are still able to extend the generated programming exercises with additional features that are not available in one specific template. diff --git a/docs/user/exercises/programming-exercise-setup.inc b/docs/user/exercises/programming-exercise-setup.inc index 563f65f48361..b95420105763 100644 --- a/docs/user/exercises/programming-exercise-setup.inc +++ b/docs/user/exercises/programming-exercise-setup.inc @@ -106,9 +106,6 @@ Generate programming exercise - **Sequential Test Runs:** Activate this option to first run structural and then behavior tests. This feature allows students to better concentrate on the immediate challenge at hand. Not supported together with static code analysis. Cannot be changed after the exercise creation. - - **Record Testwise Coverage:** Activate this option to record the testwise coverage for the solution repository. - This is necessary when working with Hestia to generate code-based hints. - This option is only available for Java/Kotlin-exercises with non-sequential test runs. - **Customize Build Plan** Activate this option if you want to customize the build plan of your exercise. This feature is available for all programming languages, and works with LocalCI and Jenkins, Artemis provides templates for the build plan configuration. The build plan can also be customized after the exercise creation. diff --git a/gradle/test.gradle b/gradle/test.gradle index 8e84af376e9f..52ef3fe25ea5 100644 --- a/gradle/test.gradle +++ b/gradle/test.gradle @@ -108,13 +108,13 @@ jacocoTestCoverageVerification { counter = "INSTRUCTION" value = "COVEREDRATIO" // TODO: in the future the following value should become higher than 0.92 - minimum = 0.895 + minimum = 0.891 } limit { counter = "CLASS" value = "MISSEDCOUNT" // TODO: in the future the following value should become less than 10 - maximum = 56 + maximum = 55 } } } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/domain/Feedback.java b/src/main/java/de/tum/cit/aet/artemis/assessment/domain/Feedback.java index de095b019216..c07579401ff0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/domain/Feedback.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/domain/Feedback.java @@ -79,7 +79,7 @@ public class Feedback extends DomainObject { * null if the feedback was not created by an automatic test case. */ @ManyToOne(fetch = FetchType.LAZY) - @JsonIgnoreProperties({ "tasks", "solutionEntries", "exercise", "coverageEntries" }) + @JsonIgnoreProperties({ "tasks", "exercise" }) private ProgrammingExerciseTestCase testCase; /** diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/domain/Result.java b/src/main/java/de/tum/cit/aet/artemis/assessment/domain/Result.java index 88d5d44e5271..ad45634f2d74 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/domain/Result.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/domain/Result.java @@ -10,10 +10,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; import jakarta.persistence.CascadeType; @@ -28,7 +26,6 @@ import jakarta.persistence.OneToMany; import jakarta.persistence.OrderColumn; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import jakarta.validation.constraints.NotNull; import org.apache.commons.lang3.StringUtils; @@ -53,7 +50,6 @@ import de.tum.cit.aet.artemis.exercise.domain.participation.Participation; import de.tum.cit.aet.artemis.exercise.service.ExerciseDateService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageFileReport; import de.tum.cit.aet.artemis.programming.dto.ResultDTO; import de.tum.cit.aet.artemis.quiz.config.QuizView; import de.tum.cit.aet.artemis.quiz.domain.QuizExercise; @@ -152,13 +148,6 @@ public class Result extends DomainObject implements Comparable { @JsonIgnore private Instant lastModifiedDate; - // This attribute is required to forward the coverage file reports after creating the build result. This is required in order to - // delay referencing the corresponding test cases from the entries because the test cases are not saved in the database - // at this point of time but the required test case name would be lost, otherwise. - @Transient - @JsonIgnore - private Map> fileReportsByTestCaseName; - public ZonedDateTime getCompletionDate() { return completionDate; } @@ -500,14 +489,6 @@ public void setCodeIssueCount(int codeIssueCount) { this.codeIssueCount = Math.min(codeIssueCount, SIZE_OF_UNSIGNED_TINYINT); } - public Map> getCoverageFileReportsByTestCaseName() { - return fileReportsByTestCaseName; - } - - public void setCoverageFileReportsByTestCaseName(Map> fileReportsByTestCaseName) { - this.fileReportsByTestCaseName = fileReportsByTestCaseName; - } - /** * Checks the initialization status of the assessment note before returning. Only a single element is returned instead of the list, * because it is modelled that way on the client-side. Jackson therefore needs a single object for the (de-)serialization. diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/service/ResultService.java b/src/main/java/de/tum/cit/aet/artemis/assessment/service/ResultService.java index 1dce0090c001..80d564e4695a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/service/ResultService.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/service/ResultService.java @@ -67,16 +67,16 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.build.BuildPlanType; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.repository.BuildJobRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseStudentParticipationRepository; import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; import de.tum.cit.aet.artemis.programming.repository.TemplateProgrammingExerciseParticipationRepository; import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseTaskService; @Profile(PROFILE_CORE) @Service diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/web/open/PublicResultResource.java b/src/main/java/de/tum/cit/aet/artemis/assessment/web/open/PublicResultResource.java index a346594c409f..c6b7a6ceb082 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/web/open/PublicResultResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/web/open/PublicResultResource.java @@ -30,7 +30,6 @@ import de.tum.cit.aet.artemis.programming.service.ProgrammingMessagingService; import de.tum.cit.aet.artemis.programming.service.ProgrammingTriggerService; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService; -import de.tum.cit.aet.artemis.programming.service.hestia.TestwiseCoverageService; /** * REST controller for receiving build results. @@ -51,19 +50,15 @@ public class PublicResultResource { private final ResultService resultService; - private final TestwiseCoverageService testwiseCoverageService; - private final ProgrammingTriggerService programmingTriggerService; private final ProgrammingMessagingService programmingMessagingService; public PublicResultResource(Optional continuousIntegrationService, ProgrammingExerciseGradingService programmingExerciseGradingService, - ResultService resultService, TestwiseCoverageService testwiseCoverageService, ProgrammingTriggerService programmingTriggerService, - ProgrammingMessagingService programmingMessagingService) { + ResultService resultService, ProgrammingTriggerService programmingTriggerService, ProgrammingMessagingService programmingMessagingService) { this.continuousIntegrationService = continuousIntegrationService; this.programmingExerciseGradingService = programmingExerciseGradingService; this.resultService = resultService; - this.testwiseCoverageService = testwiseCoverageService; this.programmingTriggerService = programmingTriggerService; this.programmingMessagingService = programmingMessagingService; } @@ -125,12 +120,6 @@ public ResponseEntity processNewProgrammingExerciseResult(@RequestHeader(" // This method will return without triggering the build if the submission is not of type TEST. var programmingSubmission = (ProgrammingSubmission) result.getSubmission(); triggerTemplateBuildIfTestCasesChanged(participation.getProgrammingExercise().getId(), programmingSubmission); - - // the test cases and the submission have been saved to the database previously, therefore we can add the reference to the coverage reports - if (Boolean.TRUE.equals(participation.getProgrammingExercise().getBuildConfig().isTestwiseCoverageEnabled()) && Boolean.TRUE.equals(result.isSuccessful())) { - testwiseCoverageService.createTestwiseCoverageReport(result.getCoverageFileReportsByTestCaseName(), participation.getProgrammingExercise(), - programmingSubmission); - } } programmingMessagingService.notifyUserAboutNewResult(result, participation); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectImportService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectImportService.java index d20c671193de..19f5081bb032 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectImportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectImportService.java @@ -55,7 +55,7 @@ import de.tum.cit.aet.artemis.plagiarism.service.PlagiarismDetectionConfigHelper; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTaskRepository; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseImportService; import de.tum.cit.aet.artemis.quiz.domain.QuizExercise; import de.tum.cit.aet.artemis.quiz.repository.QuizExerciseRepository; @@ -261,7 +261,6 @@ else if (foundByShortName.isPresent()) { private void clearProgrammingExerciseAttributes(ProgrammingExercise programmingExercise) { programmingExercise.setTasks(null); - programmingExercise.setExerciseHints(new HashSet<>()); programmingExercise.setTestCases(new HashSet<>()); programmingExercise.setStaticCodeAnalysisCategories(new HashSet<>()); programmingExercise.setTeams(new HashSet<>()); diff --git a/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/BuildConfig.java b/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/BuildConfig.java index 34d139aa6f19..4f8df8d2f49d 100644 --- a/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/BuildConfig.java +++ b/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/BuildConfig.java @@ -11,12 +11,12 @@ // NOTE: this data structure is used in shared code between core and build agent nodes. Changing it requires that the shared data structures in Hazelcast (or potentially Redis) // in the future are migrated or cleared. Changes should be communicated in release notes as potentially breaking changes. +// TODO: reduce the amount of parameters and combine some in smaller record DTOs @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record BuildConfig(String buildScript, String dockerImage, String commitHashToBuild, String assignmentCommitHash, String testCommitHash, String branch, - ProgrammingLanguage programmingLanguage, ProjectType projectType, boolean scaEnabled, boolean sequentialTestRunsEnabled, boolean testwiseCoverageEnabled, - List resultPaths, int timeoutSeconds, String assignmentCheckoutPath, String testCheckoutPath, String solutionCheckoutPath, DockerRunConfig dockerRunConfig) - implements Serializable { + ProgrammingLanguage programmingLanguage, ProjectType projectType, boolean scaEnabled, boolean sequentialTestRunsEnabled, List resultPaths, int timeoutSeconds, + String assignmentCheckoutPath, String testCheckoutPath, String solutionCheckoutPath, DockerRunConfig dockerRunConfig) implements Serializable { @Override public String dockerImage() { diff --git a/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/BuildResult.java b/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/BuildResult.java index 9d78a5fe4984..b9ab91a21301 100644 --- a/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/BuildResult.java +++ b/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/BuildResult.java @@ -3,20 +3,15 @@ import java.io.Serializable; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Collections; import java.util.List; - -import org.springframework.util.ObjectUtils; +import java.util.Objects; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; -import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO; -import de.tum.cit.aet.artemis.programming.dto.BuildJobDTOInterface; +import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification; import de.tum.cit.aet.artemis.programming.dto.StaticCodeAnalysisReportDTO; -import de.tum.cit.aet.artemis.programming.dto.TestCaseBaseDTO; -import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.TestwiseCoverageReportDTO; /** * Represents all the information returned by the local CI system about a build. @@ -27,88 +22,30 @@ // in the future are migrated or cleared. Changes should be communicated in release notes as potentially breaking changes. @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) -// TODO: this should be a record in the future -public class BuildResult extends AbstractBuildResultNotificationDTO implements Serializable { - - private final String assignmentRepoBranchName; - - private final String assignmentRepoCommitHash; - - private final String testsRepoCommitHash; - - private final boolean isBuildSuccessful; - - private final ZonedDateTime buildRunDate; - - private final List jobs; - - private List buildLogEntries = new ArrayList<>(); - - private final List staticCodeAnalysisReports; - - private boolean hasLogs = false; - - public BuildResult(String assignmentRepoBranchName, String assignmentRepoCommitHash, String testsRepoCommitHash, boolean isBuildSuccessful, ZonedDateTime buildRunDate, - List jobs, List staticCodeAnalysisReports) { - this.assignmentRepoBranchName = assignmentRepoBranchName; - this.assignmentRepoCommitHash = assignmentRepoCommitHash; - this.testsRepoCommitHash = testsRepoCommitHash; - this.isBuildSuccessful = isBuildSuccessful; - this.buildRunDate = buildRunDate; - this.jobs = jobs; - this.staticCodeAnalysisReports = staticCodeAnalysisReports; - } - - public BuildResult(String assignmentRepoBranchName, String assignmentRepoCommitHash, String testsRepoCommitHash, boolean isBuildSuccessful) { - this.assignmentRepoBranchName = assignmentRepoBranchName; - this.assignmentRepoCommitHash = assignmentRepoCommitHash; - this.testsRepoCommitHash = testsRepoCommitHash; - this.isBuildSuccessful = isBuildSuccessful; - this.buildRunDate = ZonedDateTime.now(); - this.jobs = new ArrayList<>(); - this.staticCodeAnalysisReports = new ArrayList<>(); - } +public record BuildResult(String assignmentRepoBranchName, String assignmentRepoCommitHash, String testsRepoCommitHash, boolean isBuildSuccessful, ZonedDateTime buildRunDate, + List jobs, List buildLogEntries, List staticCodeAnalysisReports, boolean hasLogs) + implements BuildResultNotification, Serializable { - @Override - public ZonedDateTime getBuildRunDate() { - return buildRunDate; - } - - @Override - protected String getCommitHashFromAssignmentRepo() { - if (ObjectUtils.isEmpty(assignmentRepoCommitHash)) { - return null; - } - return assignmentRepoCommitHash; - } - - @Override - protected String getCommitHashFromTestsRepo() { - if (ObjectUtils.isEmpty(testsRepoCommitHash)) { - return null; - } - return testsRepoCommitHash; + public BuildResult { + buildRunDate = Objects.requireNonNullElse(buildRunDate, ZonedDateTime.now()); + jobs = Objects.requireNonNullElse(jobs, new ArrayList<>()); + staticCodeAnalysisReports = Objects.requireNonNullElse(staticCodeAnalysisReports, new ArrayList<>()); + buildLogEntries = Objects.requireNonNullElse(buildLogEntries, new ArrayList<>()); + hasLogs = !buildLogEntries.isEmpty(); } - @Override - public String getBranchNameFromAssignmentRepo() { - return assignmentRepoBranchName; - } - - @Override - public boolean isBuildSuccessful() { - return isBuildSuccessful; + public BuildResult(String branch, String assignmentRepoCommitHash, String testsRepoCommitHash, List buildLogs, boolean isBuildSuccessful) { + this(branch, assignmentRepoCommitHash, testsRepoCommitHash, isBuildSuccessful, null, null, buildLogs, null, buildLogs != null && !buildLogs.isEmpty()); } @Override - public Double getBuildScore() { + public Double buildScore() { // the real score is calculated in the grading service return 0D; } /** - * Local CI does not support checking for artifacts as of now. - * TODO LOCALVC_CI: Figure out in the build process whether an artifact was created, and return true here if yes. + * NOTE: Local CI does not support checking for artifacts as of now. * * @return will always return false because LocalCI does not support checking for artifacts. */ @@ -117,81 +54,9 @@ public boolean hasArtifact() { return false; } - @Override - public boolean hasLogs() { - return hasLogs; - } - @Override public List extractBuildLogs() { // convert the buildLogEntry DTOs to BuildLogEntry objects return buildLogEntries.stream().map(log -> new BuildLogEntry(log.time(), log.log())).toList(); } - - /** - * Setter for the buildLogEntries - * - * @param buildLogEntries the buildLogEntries to be set - */ - public void setBuildLogEntries(List buildLogEntries) { - this.buildLogEntries = buildLogEntries; - hasLogs = true; - } - - @Override - public List getBuildJobs() { - return jobs; - } - - @Override - public List getStaticCodeAnalysisReports() { - return staticCodeAnalysisReports; - } - - @Override - public List getTestwiseCoverageReports() { - // TODO LOCALVC_CI: Implement testwise coverage and return the reports here. - return Collections.emptyList(); - } - - /** - * Represents all the information returned by the local CI system about a job. - * In the current implementation of local CI, there is always one job per build. - * - * @param failedTests list of failed tests. - * @param successfulTests list of successful tests. - */ - @JsonInclude(JsonInclude.Include.NON_EMPTY) - public record LocalCIJobDTO(List failedTests, List successfulTests) implements BuildJobDTOInterface, Serializable { - - @Override - public List getFailedTests() { - return failedTests; - } - - @Override - public List getSuccessfulTests() { - return successfulTests; - } - } - - /** - * Represents the information about one test case, including the test case's name and potential error messages that indicate what went wrong. - * - * @param name name of the test case. - * @param errors list of error messages. - */ - @JsonInclude(JsonInclude.Include.NON_EMPTY) - public record LocalCITestJobDTO(String name, List errors) implements TestCaseBaseDTO, Serializable { - - @Override - public String getName() { - return name; - } - - @Override - public List getTestMessages() { - return errors; - } - } } diff --git a/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/LocalCIJobDTO.java b/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/LocalCIJobDTO.java new file mode 100644 index 000000000000..27410b0b60f7 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/LocalCIJobDTO.java @@ -0,0 +1,26 @@ +package de.tum.cit.aet.artemis.buildagent.dto; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import de.tum.cit.aet.artemis.programming.dto.BuildJobInterface; + +/** + * Represents all the information returned by the local CI system about a job. + * In the current implementation of local CI, there is always one job per build. + * + * @param failedTests list of failed tests. + * @param successfulTests list of successful tests. + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record LocalCIJobDTO(List failedTests, List successfulTests) implements BuildJobInterface, Serializable { + + public LocalCIJobDTO { + failedTests = Objects.requireNonNullElse(failedTests, new ArrayList<>()); + successfulTests = Objects.requireNonNullElse(successfulTests, new ArrayList<>()); + } +} diff --git a/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/LocalCITestJobDTO.java b/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/LocalCITestJobDTO.java new file mode 100644 index 000000000000..3232501a8737 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/buildagent/dto/LocalCITestJobDTO.java @@ -0,0 +1,18 @@ +package de.tum.cit.aet.artemis.buildagent.dto; + +import java.io.Serializable; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import de.tum.cit.aet.artemis.programming.dto.TestCaseBase; + +/** + * Represents the information about one test case, including the test case's name and potential error messages that indicate what went wrong. + * + * @param name name of the test case. + * @param testMessages list of error messages. + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record LocalCITestJobDTO(String name, List testMessages) implements TestCaseBase, Serializable { +} diff --git a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java index e7fd13c7b97e..c77f26b426d1 100644 --- a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java @@ -40,7 +40,10 @@ import com.github.dockerjava.api.exception.NotFoundException; import de.tum.cit.aet.artemis.buildagent.dto.BuildJobQueueItem; +import de.tum.cit.aet.artemis.buildagent.dto.BuildLogDTO; import de.tum.cit.aet.artemis.buildagent.dto.BuildResult; +import de.tum.cit.aet.artemis.buildagent.dto.LocalCIJobDTO; +import de.tum.cit.aet.artemis.buildagent.dto.LocalCITestJobDTO; import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; import de.tum.cit.aet.artemis.core.exception.GitException; import de.tum.cit.aet.artemis.core.exception.LocalCIException; @@ -327,9 +330,9 @@ private BuildResult runScriptAndParseResults(BuildJobQueueItem buildJob, String try { testResultsTarInputStream = buildJobContainerService.getArchiveFromContainer(containerId, LOCALCI_WORKING_DIRECTORY + LOCALCI_RESULTS_DIRECTORY); + var buildLogs = buildLogsMap.getAndTruncateBuildLogs(buildJob.id()); buildResult = parseTestResults(testResultsTarInputStream, buildJob.buildConfig().branch(), assignmentRepoCommitHash, testRepoCommitHash, buildCompletedDate, - buildJob.id()); - buildResult.setBuildLogEntries(buildLogsMap.getAndTruncateBuildLogs(buildJob.id())); + buildJob.id(), buildLogs); } catch (NotFoundException e) { msg = "Could not find test results in container " + containerName; @@ -389,10 +392,10 @@ private BuildResult runScriptAndParseResults(BuildJobQueueItem buildJob, String // --- Helper methods ---- private BuildResult parseTestResults(TarArchiveInputStream testResultsTarInputStream, String assignmentRepoBranchName, String assignmentRepoCommitHash, - String testsRepoCommitHash, ZonedDateTime buildCompletedDate, String buildJobId) throws IOException { + String testsRepoCommitHash, ZonedDateTime buildCompletedDate, String buildJobId, List buildLogs) throws IOException { - List failedTests = new ArrayList<>(); - List successfulTests = new ArrayList<>(); + List failedTests = new ArrayList<>(); + List successfulTests = new ArrayList<>(); List staticCodeAnalysisReports = new ArrayList<>(); TarArchiveEntry tarEntry; @@ -434,7 +437,7 @@ private BuildResult parseTestResults(TarArchiveInputStream testResultsTarInputSt } return constructBuildResult(failedTests, successfulTests, assignmentRepoBranchName, assignmentRepoCommitHash, testsRepoCommitHash, !failedTests.isEmpty(), - buildCompletedDate, staticCodeAnalysisReports); + buildCompletedDate, staticCodeAnalysisReports, buildLogs); } private boolean isValidTestResultFile(TarArchiveEntry tarArchiveEntry) { @@ -499,7 +502,7 @@ private String readTarEntryContent(TarArchiveInputStream tarArchiveInputStream) */ private BuildResult constructFailedBuildResult(String assignmentRepoBranchName, @Nullable String assignmentRepoCommitHash, @Nullable String testsRepoCommitHash, ZonedDateTime buildRunDate) { - return constructBuildResult(List.of(), List.of(), assignmentRepoBranchName, assignmentRepoCommitHash, testsRepoCommitHash, false, buildRunDate, List.of()); + return constructBuildResult(List.of(), List.of(), assignmentRepoBranchName, assignmentRepoCommitHash, testsRepoCommitHash, false, buildRunDate, List.of(), null); } /** @@ -513,14 +516,15 @@ private BuildResult constructFailedBuildResult(String assignmentRepoBranchName, * @param isBuildSuccessful Whether the build was successful or not. * @param buildRunDate The date when the build was completed. * @param staticCodeAnalysisReports The static code analysis reports + * @param buildLogs the build logs * @return a {@link BuildResult} */ - private BuildResult constructBuildResult(List failedTests, List successfulTests, String assignmentRepoBranchName, + private BuildResult constructBuildResult(List failedTests, List successfulTests, String assignmentRepoBranchName, String assignmentRepoCommitHash, String testsRepoCommitHash, boolean isBuildSuccessful, ZonedDateTime buildRunDate, - List staticCodeAnalysisReports) { - BuildResult.LocalCIJobDTO job = new BuildResult.LocalCIJobDTO(failedTests, successfulTests); - - return new BuildResult(assignmentRepoBranchName, assignmentRepoCommitHash, testsRepoCommitHash, isBuildSuccessful, buildRunDate, List.of(job), staticCodeAnalysisReports); + List staticCodeAnalysisReports, List buildLogs) { + LocalCIJobDTO job = new LocalCIJobDTO(failedTests, successfulTests); + return new BuildResult(assignmentRepoBranchName, assignmentRepoCommitHash, testsRepoCommitHash, isBuildSuccessful, buildRunDate, List.of(job), buildLogs, + staticCodeAnalysisReports, true); } private Path cloneRepository(VcsRepositoryUri repositoryUri, @Nullable String commitHash, boolean checkout, String buildJobId) { diff --git a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java index cc9c853ec806..c833a52704ec 100644 --- a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java +++ b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java @@ -463,8 +463,7 @@ private void processBuild(BuildJobQueueItem buildJob) { buildLogsMap.removeBuildLogs(buildJob.id()); BuildResult failedResult = new BuildResult(buildJob.buildConfig().branch(), buildJob.buildConfig().assignmentCommitHash(), buildJob.buildConfig().testCommitHash(), - false); - failedResult.setBuildLogEntries(buildLogs); + buildLogs, false); ResultQueueItem resultQueueItem = new ResultQueueItem(failedResult, job, buildLogs, ex); if (processResults.get()) { diff --git a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/TestResultXmlParser.java b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/TestResultXmlParser.java index ef9190911239..51603dd8f20f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/TestResultXmlParser.java +++ b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/TestResultXmlParser.java @@ -12,7 +12,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText; -import de.tum.cit.aet.artemis.buildagent.dto.BuildResult; +import de.tum.cit.aet.artemis.buildagent.dto.LocalCITestJobDTO; public class TestResultXmlParser { @@ -75,8 +75,7 @@ public class TestResultXmlParser { * @param successfulTests A list of successful tests. This list will be populated by the method. * @throws IOException If an I/O error occurs while reading the test result file. */ - public static void processTestResultFile(String testResultFileString, List failedTests, List successfulTests) - throws IOException { + public static void processTestResultFile(String testResultFileString, List failedTests, List successfulTests) throws IOException { testResultFileString = testResultFileString.replaceAll(INVALID_XML_CHARS, ""); // The root element can be or @@ -105,7 +104,7 @@ public static void processTestResultFile(String testResultFileString, List failedTests, List successfulTests, TestSuite suite) { + private static void processTopLevelTestSuite(List failedTests, List successfulTests, TestSuite suite) { processTestSuiteWithNamePrefix(suite, failedTests, successfulTests, ""); } @@ -117,8 +116,7 @@ private static void processTopLevelTestSuite(List * @param successfulTests A list of successful tests. This list will be populated by the method. * @param outerNamePrefix The name prefix for the test suite, derived from its parent suites. */ - private static void processInnerTestSuite(TestSuite testSuite, List failedTests, List successfulTests, - String outerNamePrefix) { + private static void processInnerTestSuite(TestSuite testSuite, List failedTests, List successfulTests, String outerNamePrefix) { // namePrefix recursively accumulates all parent testsuite names seperated with dots String namePrefix; if (testSuite.name() != null) { @@ -140,18 +138,17 @@ private static void processInnerTestSuite(TestSuite testSuite, List failedTests, List successfulTests, - String namePrefix) { + private static void processTestSuiteWithNamePrefix(TestSuite testSuite, List failedTests, List successfulTests, String namePrefix) { for (TestCase testCase : testSuite.testCases()) { if (testCase.isSkipped()) { continue; } Failure failure = testCase.extractFailure(); if (failure != null) { - failedTests.add(new BuildResult.LocalCITestJobDTO(namePrefix + testCase.name(), List.of(failure.extractMessage()))); + failedTests.add(new LocalCITestJobDTO(namePrefix + testCase.name(), List.of(failure.extractMessage()))); } else { - successfulTests.add(new BuildResult.LocalCITestJobDTO(namePrefix + testCase.name(), List.of())); + successfulTests.add(new LocalCITestJobDTO(namePrefix + testCase.name(), List.of())); } } @@ -190,11 +187,11 @@ private boolean isSkipped() { private Failure extractFailure() { return failure != null ? failure : error; } - } - // Intentionally empty record to represent the skipped tag () - @JsonIgnoreProperties(ignoreUnknown = true) - record Skip() { + // Intentionally empty record to represent the skipped tag () + @JsonIgnoreProperties(ignoreUnknown = true) + record Skip() { + } } // Due to issues with Jackson this currently cannot be a record. diff --git a/src/main/java/de/tum/cit/aet/artemis/communication/service/conversation/ConversationService.java b/src/main/java/de/tum/cit/aet/artemis/communication/service/conversation/ConversationService.java index 6dd36e5e9bac..b9fbbf97715c 100644 --- a/src/main/java/de/tum/cit/aet/artemis/communication/service/conversation/ConversationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/communication/service/conversation/ConversationService.java @@ -164,11 +164,11 @@ public Conversation loadConversationWithParticipantsIfGroupChat(Long conversatio } /** - * Gets the conversation in a course for which the user is a member + * Gets the conversations in a course for which the user is a member * * @param course the course * @param requestingUser the user for which the conversations are requested - * @return the conversation in the course for which the user is a member + * @return list of conversations in the course for which the user is a member */ public List getConversationsOfUser(Course course, User requestingUser) { var conversationsOfUser = new ArrayList(); diff --git a/src/main/java/de/tum/cit/aet/artemis/core/repository/UserRepository.java b/src/main/java/de/tum/cit/aet/artemis/core/repository/UserRepository.java index 0d3280cf5d96..6eb94330acff 100644 --- a/src/main/java/de/tum/cit/aet/artemis/core/repository/UserRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/core/repository/UserRepository.java @@ -920,7 +920,7 @@ private String getCurrentUserLogin() { } /** - * Get user with user groups and authorities with the username (i.e. user.getLogin() or principal.getName()) + * Get user with user groups and authorities with the username (i.e. user.getLogin() or principal.name()) * * @param username the username of the user who should be retrieved from the database * @return the user that belongs to the given principal with eagerly loaded groups and authorities diff --git a/src/main/java/de/tum/cit/aet/artemis/core/service/TitleCacheEvictionService.java b/src/main/java/de/tum/cit/aet/artemis/core/service/TitleCacheEvictionService.java index 0ee7cceac695..67c43ec410c9 100644 --- a/src/main/java/de/tum/cit/aet/artemis/core/service/TitleCacheEvictionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/core/service/TitleCacheEvictionService.java @@ -29,7 +29,6 @@ import de.tum.cit.aet.artemis.exercise.domain.Exercise; import de.tum.cit.aet.artemis.lecture.domain.Lecture; import de.tum.cit.aet.artemis.modeling.domain.ApollonDiagram; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; /** * Listens to Hibernate events and invalidates the cached titles of an entity if the title changed. @@ -121,15 +120,6 @@ else if (entity instanceof ApollonDiagram diagram) { else if (entity instanceof Exam exam) { evictIdFromCache("examTitle", exam.getId()); } - else if (entity instanceof ExerciseHint hint) { - if (hint.getExercise() == null) { - log.warn("Unable to clear title of exercise hint {}: Exercise not present", hint.getId()); - return; - } - - var combinedId = hint.getExercise().getId() + "-" + hint.getId(); - evictIdFromCache("exerciseHintTitle", combinedId); - } else if (entity instanceof ExerciseGroup exerciseGroup) { if (!Hibernate.isInitialized(exerciseGroup.getExercises())) { log.warn("Unable to clear title of exercises from exercise group {}: Exercises not initialized", exerciseGroup.getId()); diff --git a/src/main/java/de/tum/cit/aet/artemis/exam/service/ExamImportService.java b/src/main/java/de/tum/cit/aet/artemis/exam/service/ExamImportService.java index 58fcf03cc97a..507a5c706975 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exam/service/ExamImportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exam/service/ExamImportService.java @@ -33,7 +33,7 @@ import de.tum.cit.aet.artemis.modeling.service.ModelingExerciseImportService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTaskRepository; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseImportService; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseService; import de.tum.cit.aet.artemis.quiz.domain.QuizExercise; diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/dto/ExerciseDetailsDTO.java b/src/main/java/de/tum/cit/aet/artemis/exercise/dto/ExerciseDetailsDTO.java index eaa46988ebda..c577b573ee6f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/dto/ExerciseDetailsDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/dto/ExerciseDetailsDTO.java @@ -1,15 +1,11 @@ package de.tum.cit.aet.artemis.exercise.dto; -import java.util.Set; - import com.fasterxml.jackson.annotation.JsonInclude; import de.tum.cit.aet.artemis.exercise.domain.Exercise; import de.tum.cit.aet.artemis.iris.dto.IrisCombinedSettingsDTO; import de.tum.cit.aet.artemis.plagiarism.dto.PlagiarismCaseInfoDTO; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record ExerciseDetailsDTO(Exercise exercise, IrisCombinedSettingsDTO irisSettings, PlagiarismCaseInfoDTO plagiarismCaseInfo, Set availableExerciseHints, - Set activatedExerciseHints) { +public record ExerciseDetailsDTO(Exercise exercise, IrisCombinedSettingsDTO irisSettings, PlagiarismCaseInfoDTO plagiarismCaseInfo) { } diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ParticipationService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ParticipationService.java index a8c4d3507813..3f3d8d43257e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ParticipationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ParticipationService.java @@ -50,7 +50,6 @@ import de.tum.cit.aet.artemis.programming.repository.BuildLogStatisticsEntryRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseStudentParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageReportRepository; import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; import de.tum.cit.aet.artemis.programming.service.GitService; import de.tum.cit.aet.artemis.programming.service.ParticipationVcsAccessTokenService; @@ -93,8 +92,6 @@ public class ParticipationService { private final ResultService resultService; - private final CoverageReportRepository coverageReportRepository; - private final BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository; private final ParticipantScoreRepository participantScoreRepository; @@ -115,10 +112,9 @@ public ParticipationService(GitService gitService, Optional localCISharedBuildJobQueueService, ProfileService profileService, - ParticipationVcsAccessTokenService participationVCSAccessTokenService, CompetencyProgressApi competencyProgressApi) { + BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, ParticipantScoreRepository participantScoreRepository, + StudentScoreRepository studentScoreRepository, TeamScoreRepository teamScoreRepository, Optional localCISharedBuildJobQueueService, + ProfileService profileService, ParticipationVcsAccessTokenService participationVCSAccessTokenService, CompetencyProgressApi competencyProgressApi) { this.gitService = gitService; this.continuousIntegrationService = continuousIntegrationService; this.versionControlService = versionControlService; @@ -131,7 +127,6 @@ public ParticipationService(GitService gitService, Optional irisSettingsService, - PlagiarismCaseService plagiarismCaseService, ExerciseHintService exerciseHintService) { + PlagiarismCaseService plagiarismCaseService) { this.exerciseService = exerciseService; this.exerciseDeletionService = exerciseDeletionService; this.participationService = participationService; @@ -128,7 +124,6 @@ public ExerciseResource(ExerciseService exerciseService, ExerciseDeletionService this.examAccessService = examAccessService; this.irisSettingsService = irisSettingsService; this.plagiarismCaseService = plagiarismCaseService; - this.exerciseHintService = exerciseHintService; } /** @@ -349,13 +344,7 @@ public ResponseEntity getExerciseDetails(@PathVariable Long .orElse(null); PlagiarismCaseInfoDTO plagiarismCaseInfo = plagiarismCaseService.getPlagiarismCaseInfoForExerciseAndUser(exercise.getId(), user.getId()).orElse(null); - if (exercise instanceof ProgrammingExercise programmingExercise) { - Set activatedExerciseHints = exerciseHintService.getActivatedExerciseHints(programmingExercise, user); - Set availableExerciseHints = exerciseHintService.getAvailableExerciseHints(programmingExercise, user); - return ResponseEntity.ok(new ExerciseDetailsDTO(exercise, irisSettings, plagiarismCaseInfo, availableExerciseHints, activatedExerciseHints)); - } - - return ResponseEntity.ok(new ExerciseDetailsDTO(exercise, irisSettings, plagiarismCaseInfo, null, null)); + return ResponseEntity.ok(new ExerciseDetailsDTO(exercise, irisSettings, plagiarismCaseInfo)); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/lti/dto/Scopes.java b/src/main/java/de/tum/cit/aet/artemis/lti/dto/Scopes.java index ff34ce2fc460..735bbf7d15d3 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lti/dto/Scopes.java +++ b/src/main/java/de/tum/cit/aet/artemis/lti/dto/Scopes.java @@ -1,6 +1,6 @@ package de.tum.cit.aet.artemis.lti.dto; -public class Scopes { +public final class Scopes { public static final String AGS_SCORE = "https://purl.imsglobal.org/spec/lti-ags/scope/score"; 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 a1b16c64ade3..cec8919e81dd 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 @@ -59,8 +59,8 @@ import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; import de.tum.cit.aet.artemis.programming.service.GitService; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseExportService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseGitDiffReportService; import de.tum.cit.aet.artemis.programming.service.UriService; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseGitDiffReportService; @Profile(PROFILE_CORE) @Service diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExercise.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExercise.java index c2a4666c7c1b..63d247ffdfc8 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExercise.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExercise.java @@ -49,8 +49,6 @@ import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; import de.tum.cit.aet.artemis.exercise.service.ExerciseDateService; import de.tum.cit.aet.artemis.programming.domain.build.BuildPlanType; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.SubmissionPolicy; import de.tum.cit.aet.artemis.programming.service.ProgrammingLanguageFeature; @@ -149,9 +147,6 @@ public String getType() { @Column(name = "project_type", table = "programming_exercise_details") private ProjectType projectType; - @OneToMany(mappedBy = "exercise", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY) - private Set exerciseHints = new HashSet<>(); - @Column(name = "release_tests_with_example_solution", table = "programming_exercise_details", nullable = false) private boolean releaseTestsWithExampleSolution = false; @@ -815,21 +810,12 @@ public void validateSettingsForFeedbackRequest() { } } - public Set getExerciseHints() { - return exerciseHints; - } - - public void setExerciseHints(Set exerciseHints) { - this.exerciseHints = exerciseHints; - } - /** * {@inheritDoc} */ @Override public void disconnectRelatedEntities() { - Stream.of(exerciseHints, testCases, staticCodeAnalysisCategories).filter(Objects::nonNull).forEach(Collection::clear); - + Stream.of(testCases, staticCodeAnalysisCategories).filter(Objects::nonNull).forEach(Collection::clear); super.disconnectRelatedEntities(); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseBuildConfig.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseBuildConfig.java index d28e21bb3ad1..1a784bdf060d 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseBuildConfig.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseBuildConfig.java @@ -19,7 +19,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import de.tum.cit.aet.artemis.core.domain.DomainObject; -import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; import de.tum.cit.aet.artemis.programming.service.vcs.AbstractVersionControlService; @Entity @@ -67,9 +67,6 @@ public class ProgrammingExerciseBuildConfig extends DomainObject { @JsonIgnoreProperties("buildConfig") private ProgrammingExercise programmingExercise; - @Column(name = "testwise_coverage_enabled") - private boolean testwiseCoverageEnabled; - @Nullable @Column(name = "theia_image") private String theiaImage; @@ -98,7 +95,6 @@ public ProgrammingExerciseBuildConfig(ProgrammingExerciseBuildConfig originalBui this.setDockerFlags(originalBuildConfig.getDockerFlags()); this.setSequentialTestRuns(originalBuildConfig.hasSequentialTestRuns()); this.setBuildScript(originalBuildConfig.getBuildScript()); - this.setTestwiseCoverageEnabled(originalBuildConfig.isTestwiseCoverageEnabled()); this.setTimeoutSeconds(originalBuildConfig.getTimeoutSeconds()); this.setTheiaImage(originalBuildConfig.getTheiaImage()); this.setAllowBranching(originalBuildConfig.isAllowBranching()); @@ -247,14 +243,6 @@ public void filterSensitiveInformation() { setBuildScript(null); } - public boolean isTestwiseCoverageEnabled() { - return testwiseCoverageEnabled; - } - - public void setTestwiseCoverageEnabled(boolean testwiseCoverageEnabled) { - this.testwiseCoverageEnabled = testwiseCoverageEnabled; - } - public ProgrammingExercise getProgrammingExercise() { return programmingExercise; } @@ -296,7 +284,7 @@ public void setSolutionCheckoutPath(String solutionCheckoutPath) { public String toString() { return "BuildJobConfig{" + "id=" + getId() + ", sequentialTestRuns=" + sequentialTestRuns + ", branch='" + branch + '\'' + ", buildPlanConfiguration='" + buildPlanConfiguration + '\'' + ", buildScript='" + buildScript + '\'' + ", checkoutSolutionRepository=" + checkoutSolutionRepository + ", checkoutPath='" - + testCheckoutPath + '\'' + ", timeoutSeconds=" + timeoutSeconds + ", dockerFlags='" + dockerFlags + '\'' + ", testwiseCoverageEnabled=" + testwiseCoverageEnabled - + ", theiaImage='" + theiaImage + '\'' + ", allowBranching=" + allowBranching + ", branchRegex='" + branchRegex + '\'' + '}'; + + testCheckoutPath + '\'' + ", timeoutSeconds=" + timeoutSeconds + ", dockerFlags='" + dockerFlags + '\'' + ", theiaImage='" + theiaImage + '\'' + + ", allowBranching=" + allowBranching + ", branchRegex='" + branchRegex + '\'' + '}'; } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseGitDiffEntry.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseGitDiffEntry.java similarity index 98% rename from src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseGitDiffEntry.java rename to src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseGitDiffEntry.java index 23095ebaea55..f1ff24c6c144 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseGitDiffEntry.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseGitDiffEntry.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.domain.hestia; +package de.tum.cit.aet.artemis.programming.domain; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseGitDiffReport.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseGitDiffReport.java similarity index 95% rename from src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseGitDiffReport.java rename to src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseGitDiffReport.java index b63d3fd713b1..d69f509ecf9e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseGitDiffReport.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseGitDiffReport.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.domain.hestia; +package de.tum.cit.aet.artemis.programming.domain; import java.util.Set; @@ -17,7 +17,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; import de.tum.cit.aet.artemis.core.domain.DomainObject; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; /** * A git-diff report representing a git-diff between the template and solution repositories of a ProgrammingExercise. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseTask.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseTask.java similarity index 73% rename from src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseTask.java rename to src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseTask.java index 4a7a26ddee49..9b5c0d95b29d 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseTask.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseTask.java @@ -1,9 +1,8 @@ -package de.tum.cit.aet.artemis.programming.domain.hestia; +package de.tum.cit.aet.artemis.programming.domain; import java.util.HashSet; import java.util.Set; -import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -11,7 +10,6 @@ import jakarta.persistence.JoinTable; import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import org.hibernate.annotations.Cache; @@ -22,8 +20,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; import de.tum.cit.aet.artemis.core.domain.DomainObject; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; /** * A ProgrammingExerciseTask @@ -37,11 +33,6 @@ public class ProgrammingExerciseTask extends DomainObject { @Column(name = "task_name") private String taskName; - // No orphanRemoval here, as there should only be one parent-child relationship (which is ProgrammingExercise -> ExerciseHint) - @OneToMany(mappedBy = "task", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY) - @JsonIgnoreProperties("task") - private Set exerciseHints = new HashSet<>(); - @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "programming_exercise_task_test_case", joinColumns = @JoinColumn(name = "task_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "test_case_id", referencedColumnName = "id")) @JsonIgnoreProperties(value = { "tasks", "exercise" }, allowSetters = true) @@ -59,14 +50,6 @@ public void setTaskName(String taskName) { this.taskName = taskName; } - public Set getExerciseHints() { - return exerciseHints; - } - - public void setExerciseHints(Set exerciseHints) { - this.exerciseHints = exerciseHints; - } - public Set getTestCases() { return this.testCases; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseTestCase.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseTestCase.java index dae43fce9dd9..3da8bde413d7 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseTestCase.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseTestCase.java @@ -1,11 +1,10 @@ package de.tum.cit.aet.artemis.programming.domain; -import static de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTestCaseType.DEFAULT; +import static de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCaseType.DEFAULT; import java.util.HashSet; import java.util.Set; -import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; @@ -15,7 +14,6 @@ import jakarta.persistence.JoinTable; import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; @@ -31,10 +29,6 @@ import de.tum.cit.aet.artemis.assessment.domain.Visibility; import de.tum.cit.aet.artemis.core.domain.DomainObject; import de.tum.cit.aet.artemis.exercise.domain.Exercise; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTestCaseType; -import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; /** * A ProgrammingExerciseTestCase. @@ -69,10 +63,6 @@ public class ProgrammingExerciseTestCase extends DomainObject { @JsonIgnoreProperties({ "testCases", "exercise" }) private Set tasks = new HashSet<>(); - @OneToMany(mappedBy = "testCase", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY) - @JsonIgnoreProperties(value = "testCase", allowSetters = true) - private Set solutionEntries = new HashSet<>(); - @ManyToOne(fetch = FetchType.LAZY) @JsonIgnoreProperties("testCases") private ProgrammingExercise exercise; @@ -81,10 +71,6 @@ public class ProgrammingExerciseTestCase extends DomainObject { @Column(name = "test_case_type", nullable = false) private ProgrammingExerciseTestCaseType type = DEFAULT; // default value - @OneToMany(mappedBy = "testCase", fetch = FetchType.LAZY) - @JsonIgnoreProperties("testCase") - private Set coverageEntries; - public ProgrammingExerciseTestCase id(Long id) { setId(id); return this; @@ -152,14 +138,6 @@ public void setTasks(Set tasks) { this.tasks = tasks; } - public Set getSolutionEntries() { - return solutionEntries; - } - - public void setSolutionEntries(Set solutionEntries) { - this.solutionEntries = solutionEntries; - } - public Boolean isActive() { return active; } @@ -217,14 +195,6 @@ public void setType(ProgrammingExerciseTestCaseType programmingExerciseTestCaseT this.type = programmingExerciseTestCaseType; } - public Set getCoverageEntries() { - return coverageEntries; - } - - public void setCoverageEntries(Set coverageEntries) { - this.coverageEntries = coverageEntries; - } - /** * Needs to be checked and updated if there is a new class attribute. Creates a clone with all attributes set to the value of the object, including the id. * diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseTestCaseType.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseTestCaseType.java similarity index 77% rename from src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseTestCaseType.java rename to src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseTestCaseType.java index 047a3ca73eda..3996ca815ec5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseTestCaseType.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseTestCaseType.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.domain.hestia; +package de.tum.cit.aet.artemis.programming.domain; /** * Used to define the type of a ProgrammingExerciseTestCase. @@ -14,7 +14,7 @@ public enum ProgrammingExerciseTestCaseType { */ BEHAVIORAL, /** - * Type for all programming exercises not supported by Hestia + * All other types */ DEFAULT, } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/CodeHint.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/CodeHint.java deleted file mode 100644 index a43ba0ac9c3f..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/CodeHint.java +++ /dev/null @@ -1,69 +0,0 @@ -package de.tum.cit.aet.artemis.programming.domain.hestia; - -import java.util.HashSet; -import java.util.Set; - -import jakarta.persistence.DiscriminatorValue; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.OneToMany; -import jakarta.persistence.PreRemove; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - -/** - * A CodeHint. - */ -@Entity -@DiscriminatorValue("C") -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class CodeHint extends ExerciseHint { - - // No CascadeType.REMOVE here, as we want to retain the solution entries when a code hint is deleted - @OneToMany(mappedBy = "codeHint", fetch = FetchType.LAZY) - @JsonIgnoreProperties(value = { "codeHint" }, allowSetters = true) - private Set solutionEntries = new HashSet<>(); - - public Set getSolutionEntries() { - return this.solutionEntries; - } - - public void setSolutionEntries(Set solutionEntries) { - this.solutionEntries = solutionEntries; - } - - /** - * This method ensures that all solution entry references are removed before deleting a CodeHint - */ - @PreRemove - public void preRemove() { - solutionEntries.forEach(solutionEntry -> solutionEntry.setCodeHint(null)); - } - - @Override - public void removeContent() { - super.removeContent(); - setSolutionEntries(new HashSet<>()); - } - - @Override - public String toString() { - return "CodeHint{" + "id=" + getId() + ", title='" + getTitle() + "}"; - } - - /** - * Creates a copy of this hint including basic attributes, but excluding attributes referencing other models - * - * @return The copied hint - */ - @Override - public CodeHint createCopy() { - CodeHint copiedHint = new CodeHint(); - - copiedHint.setDescription(this.getDescription()); - copiedHint.setContent(this.getContent()); - copiedHint.setTitle(this.getTitle()); - return copiedHint; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/CoverageFileReport.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/CoverageFileReport.java deleted file mode 100644 index 518c4bad4550..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/CoverageFileReport.java +++ /dev/null @@ -1,86 +0,0 @@ -package de.tum.cit.aet.artemis.programming.domain.hestia; - -import java.util.Set; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; - -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.cit.aet.artemis.core.domain.DomainObject; - -@Entity -@Table(name = "coverage_file_report") -@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class CoverageFileReport extends DomainObject { - - @ManyToOne - @JsonIgnoreProperties("fileReports") - private CoverageReport fullReport; - - @Column(name = "file_path") - private String filePath; - - // The number of lines in the whole file - @Column(name = "line_count") - private Integer lineCount; - - // The number of lines that are covered by test cases. The number refers to the unique count, meaning the same line - // that is covered in multiple entries will only count as one line - @Column(name = "covered_line_count") - private Integer coveredLineCount; - - @OneToMany(mappedBy = "fileReport", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) - @JsonIgnoreProperties(value = { "fileReport" }, allowSetters = true) - private Set testwiseCoverageEntries; - - public CoverageReport getFullReport() { - return fullReport; - } - - public void setFullReport(CoverageReport fullReport) { - this.fullReport = fullReport; - } - - public String getFilePath() { - return filePath; - } - - public void setFilePath(String filePath) { - this.filePath = filePath; - } - - public Integer getLineCount() { - return lineCount; - } - - public void setLineCount(Integer lineCount) { - this.lineCount = lineCount; - } - - public Integer getCoveredLineCount() { - return coveredLineCount; - } - - public void setCoveredLineCount(Integer coveredLineCount) { - this.coveredLineCount = coveredLineCount; - } - - public Set getTestwiseCoverageEntries() { - return testwiseCoverageEntries; - } - - public void setTestwiseCoverageEntries(Set testwiseCoverageEntries) { - this.testwiseCoverageEntries = testwiseCoverageEntries; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/CoverageReport.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/CoverageReport.java deleted file mode 100644 index 9349483cb4e6..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/CoverageReport.java +++ /dev/null @@ -1,75 +0,0 @@ -package de.tum.cit.aet.artemis.programming.domain.hestia; - -import java.util.Set; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToMany; -import jakarta.persistence.OneToOne; -import jakarta.persistence.Table; - -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.cit.aet.artemis.core.domain.DomainObject; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; - -/** - * A testwise coverage report representing the executed code by file path of a single ProgrammingExerciseTestCase. - * The entries contain the information about executed code by the start line and the length (i.e. number of lines) of - * a consecutively executed block. - */ -@Entity -@Table(name = "coverage_report") -@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class CoverageReport extends DomainObject { - - // The ProgrammingSubmission to which this CoverageReport is related. This ProgrammingSubmission is always related - // to a SolutionProgrammingExerciseParticipation because the report will only be generated for solution participations. - @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "submission_id", referencedColumnName = "id") - @JsonIgnore - private ProgrammingSubmission submission; - - // When retrieving only the aggregated data (such as covered line ratio), the file reports are not required - @OneToMany(mappedBy = "fullReport", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) - @JsonIgnoreProperties(value = { "fullReport" }, allowSetters = true) - private Set fileReports; - - // The ratio between the number of lines that are covered by all tests and the number of lines for all files in - // the corresponding (solution) submission. The attribute can take values within the range of [0, 1]. - @Column(name = "covered_line_ratio") - private Double coveredLineRatio; - - public ProgrammingSubmission getSubmission() { - return submission; - } - - public void setSubmission(ProgrammingSubmission submission) { - this.submission = submission; - } - - public Set getFileReports() { - return fileReports; - } - - public void setFileReports(Set fileReports) { - this.fileReports = fileReports; - } - - public Double getCoveredLineRatio() { - return coveredLineRatio; - } - - public void setCoveredLineRatio(Double coveredLineRatio) { - this.coveredLineRatio = coveredLineRatio; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ExerciseHint.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ExerciseHint.java deleted file mode 100644 index 119abdf09c53..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ExerciseHint.java +++ /dev/null @@ -1,192 +0,0 @@ -package de.tum.cit.aet.artemis.programming.domain.hestia; - -import java.util.HashSet; -import java.util.Set; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.DiscriminatorColumn; -import jakarta.persistence.DiscriminatorType; -import jakarta.persistence.DiscriminatorValue; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.Inheritance; -import jakarta.persistence.InheritanceType; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; -import jakarta.persistence.Transient; -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; - -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; - -import de.tum.cit.aet.artemis.core.domain.DomainObject; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.service.hestia.ExerciseHintService; - -/** - * An ExerciseHint. - */ -@Entity -@Table(name = "exercise_hint") -@Inheritance(strategy = InheritanceType.SINGLE_TABLE) -@DiscriminatorColumn(name = "discriminator", discriminatorType = DiscriminatorType.STRING) -@DiscriminatorValue("T") -@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -// @formatter:off -@JsonSubTypes({ - @JsonSubTypes.Type(value = ExerciseHint.class, name = "text"), - @JsonSubTypes.Type(value = CodeHint.class, name = "code") -}) -// @formatter:on -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class ExerciseHint extends DomainObject { - - @Column(name = "title") - private String title; - - // A short description of this hint, so the student knows what this hint is about before activating it - @Column(name = "description", length = 1000) - private String description; - - @Column(name = "content") - private String content; - - @ManyToOne - @JsonIgnoreProperties("exerciseHints") - private ProgrammingExercise exercise; - - @ManyToOne - @JsonIgnoreProperties("exerciseHints") - private ProgrammingExerciseTask task; - - @OneToMany(mappedBy = "exerciseHint", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY) - @JsonIgnore - private Set exerciseHintActivations = new HashSet<>(); - - @Column(name = "display_threshold", columnDefinition = "TINYINT") - @Min(0) - @Max(100) - private short displayThreshold = 3; - - @Transient - private Integer currentUserRatingTransient; - - public String getTitle() { - return title; - } - - public ExerciseHint title(String title) { - this.title = title; - return this; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getContent() { - return content; - } - - public ExerciseHint content(String content) { - this.content = content; - return this; - } - - public void setContent(String content) { - this.content = content; - } - - public ProgrammingExercise getExercise() { - return exercise; - } - - public ExerciseHint exercise(ProgrammingExercise exercise) { - this.exercise = exercise; - return this; - } - - public ProgrammingExerciseTask getProgrammingExerciseTask() { - return task; - } - - public void setProgrammingExerciseTask(ProgrammingExerciseTask programmingExerciseTask) { - this.task = programmingExerciseTask; - } - - public void setExercise(ProgrammingExercise exercise) { - this.exercise = exercise; - } - - public void removeContent() { - this.content = null; - this.title = null; - } - - @Override - public String toString() { - return "ExerciseHint{" + "id=" + getId() + ", title='" + getTitle() + "'" + ", content='" + getContent() + "'" + "}"; - } - - public Integer getCurrentUserRating() { - return currentUserRatingTransient; - } - - public void setCurrentUserRating(Integer currentUserRating) { - this.currentUserRatingTransient = currentUserRating; - } - - public Set getExerciseHintActivations() { - return exerciseHintActivations; - } - - public void setExerciseHintActivations(Set exerciseHintActivations) { - this.exerciseHintActivations = exerciseHintActivations; - } - - /** - * Returns a threshold value that defines when this exercise hint is displayed to student participating in a programming exercise. - * The algorithm defining if the hint is display is described in {@link ExerciseHintService#getAvailableExerciseHints} - * - * @return the display threshold value - */ - public short getDisplayThreshold() { - return displayThreshold; - } - - public void setDisplayThreshold(short displayThreshold) { - this.displayThreshold = displayThreshold; - } - - /** - * Creates a copy of this hint including basic attributes, but excluding attributes referencing other models - * - * @return The copied hint - */ - public ExerciseHint createCopy() { - ExerciseHint copiedHint = new ExerciseHint(); - - copiedHint.setDescription(this.getDescription()); - copiedHint.setContent(this.getContent()); - copiedHint.setTitle(this.getTitle()); - return copiedHint; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ExerciseHintActivation.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ExerciseHintActivation.java deleted file mode 100644 index 595c90d61abb..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ExerciseHintActivation.java +++ /dev/null @@ -1,67 +0,0 @@ -package de.tum.cit.aet.artemis.programming.domain.hestia; - -import java.time.ZonedDateTime; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; - -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.cit.aet.artemis.core.domain.DomainObject; -import de.tum.cit.aet.artemis.core.domain.User; - -@Entity -@Table(name = "exercise_hint_activation") -@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class ExerciseHintActivation extends DomainObject { - - @ManyToOne(optional = false) - private User user; - - @ManyToOne(optional = false) - private ExerciseHint exerciseHint; - - @Column(name = "activation_date", nullable = false) - private ZonedDateTime activationDate; - - @Column(name = "rating") - private Integer rating; - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - public ExerciseHint getExerciseHint() { - return exerciseHint; - } - - public void setExerciseHint(ExerciseHint exerciseHint) { - this.exerciseHint = exerciseHint; - } - - public ZonedDateTime getActivationDate() { - return activationDate; - } - - public void setActivationDate(ZonedDateTime activationDate) { - this.activationDate = activationDate; - } - - public Integer getRating() { - return rating; - } - - public void setRating(Integer rating) { - this.rating = rating; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseSolutionEntry.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseSolutionEntry.java deleted file mode 100644 index 01cf4ce871b4..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/ProgrammingExerciseSolutionEntry.java +++ /dev/null @@ -1,135 +0,0 @@ -package de.tum.cit.aet.artemis.programming.domain.hestia; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; - -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -import de.tum.cit.aet.artemis.core.domain.DomainObject; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; - -/** - * A ProgrammingExerciseSolutionEntry represents a single change in a file that a students has to make in order to pass the related test. - * It is structured similarly to a git diff entry. - * If it replaces existing code it will contain the previous code that it replaces otherwise previousCode will be null. - * If it is only removing existing code the code attribute will be null. - * If it encompasses the addition of an entire file, previousLine will be null. - * If it deletes an entire file, line will be null. - * previousLine and line will be different when there are other changes higher up in the file. - *

- * Example: - * A print statement gets changed: - * - * SolutionEntry { - * filePath = "<...>" - * previousLine = 12 - * previousCode = System.out.println("Tset"); - * line = 12 - * code = System.out.println("Test"); - * } - * - */ -@Entity -@Table(name = "programming_exercise_solution_entry") -@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class ProgrammingExerciseSolutionEntry extends DomainObject { - - @Column(name = "file_path") - private String filePath; - - // The line at which the previous code segment is in the template - @Column(name = "previous_line") - private Integer previousLine; - - // The line at which the new code segment is in the solution - @Column(name = "line") - private Integer line; - - // The previous code segment to be replaced by the new code segment - @Column(name = "previous_code") - private String previousCode; - - // The new code segment that replaces the old code segment - @Column(name = "code") - private String code; - - // Fetched lazily, as we never need the code hint when fetching solution entries - @ManyToOne(fetch = FetchType.LAZY) - @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) - private CodeHint codeHint; - - @ManyToOne - @JsonIgnoreProperties(value = "solutionEntries", allowSetters = true) - private ProgrammingExerciseTestCase testCase; - - public String getFilePath() { - return filePath; - } - - public void setFilePath(String file) { - this.filePath = file; - } - - public Integer getPreviousLine() { - return previousLine; - } - - public void setPreviousLine(Integer previousLine) { - this.previousLine = previousLine; - } - - public Integer getLine() { - return line; - } - - public void setLine(Integer line) { - this.line = line; - } - - public String getPreviousCode() { - return previousCode; - } - - public void setPreviousCode(String previousCode) { - this.previousCode = previousCode; - } - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public CodeHint getCodeHint() { - return codeHint; - } - - public void setCodeHint(CodeHint codeHint) { - this.codeHint = codeHint; - } - - public ProgrammingExerciseTestCase getTestCase() { - return this.testCase; - } - - public void setTestCase(ProgrammingExerciseTestCase testCase) { - this.testCase = testCase; - } - - @Override - public String toString() { - return "ProgrammingExerciseSolutionEntry{" + "id=" + getId() + '\'' + ", filePath='" + filePath + '\'' + ", previousLine=" + previousLine + ", line=" + line - + ", previousCode='" + previousCode + '\'' + ", code='" + code + '\'' + '}'; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/TestwiseCoverageReportEntry.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/TestwiseCoverageReportEntry.java deleted file mode 100644 index 60383357c9dc..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/hestia/TestwiseCoverageReportEntry.java +++ /dev/null @@ -1,72 +0,0 @@ -package de.tum.cit.aet.artemis.programming.domain.hestia; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; - -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.cit.aet.artemis.core.domain.DomainObject; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; - -/** - * A single entry from testwise coverage report by file path and consecutive executed code block. - * A block is represented by the start line and the length (i.e. number of lines) of the block. - */ -@Entity -@Table(name = "testwise_coverage_report_entry") -@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class TestwiseCoverageReportEntry extends DomainObject { - - @ManyToOne - @JsonIgnoreProperties("testwiseCoverageEntries") - private CoverageFileReport fileReport; - - @ManyToOne - @JsonIgnoreProperties("coverageEntries") - private ProgrammingExerciseTestCase testCase; - - @Column(name = "start_line") - private Integer startLine; - - @Column(name = "line_count") - private Integer lineCount; - - public CoverageFileReport getFileReport() { - return fileReport; - } - - public void setFileReport(CoverageFileReport fileReport) { - this.fileReport = fileReport; - } - - public ProgrammingExerciseTestCase getTestCase() { - return testCase; - } - - public void setTestCase(ProgrammingExerciseTestCase testCase) { - this.testCase = testCase; - } - - public Integer getStartLine() { - return startLine; - } - - public void setStartLine(Integer startLine) { - this.startLine = startLine; - } - - public Integer getLineCount() { - return lineCount; - } - - public void setLineCount(Integer lineCount) { - this.lineCount = lineCount; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/dto/BuildJobDTOInterface.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/BuildJobInterface.java similarity index 74% rename from src/main/java/de/tum/cit/aet/artemis/programming/dto/BuildJobDTOInterface.java rename to src/main/java/de/tum/cit/aet/artemis/programming/dto/BuildJobInterface.java index 3d838ca6233b..0a8a7362deef 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/dto/BuildJobDTOInterface.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/BuildJobInterface.java @@ -8,19 +8,19 @@ * Interface for DTOs that represent a build job. */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public interface BuildJobDTOInterface { +public interface BuildJobInterface { /** * Gets the failed tests of the build job. * * @return list of failed tests. */ - List getFailedTests(); + List failedTests(); /** * Gets the successful tests of the build job. * * @return list of successful tests. */ - List getSuccessfulTests(); + List successfulTests(); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/dto/AbstractBuildResultNotificationDTO.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/BuildResultNotification.java similarity index 54% rename from src/main/java/de/tum/cit/aet/artemis/programming/dto/AbstractBuildResultNotificationDTO.java rename to src/main/java/de/tum/cit/aet/artemis/programming/dto/BuildResultNotification.java index 9435fdf8e463..bfad4eba2de8 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/dto/AbstractBuildResultNotificationDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/BuildResultNotification.java @@ -11,27 +11,25 @@ import de.tum.cit.aet.artemis.exercise.domain.SubmissionType; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; -import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.TestwiseCoverageReportDTO; @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) -// TODO: convert subclasses to records -public abstract class AbstractBuildResultNotificationDTO { +public interface BuildResultNotification { - public abstract ZonedDateTime getBuildRunDate(); + ZonedDateTime buildRunDate(); @Nullable - protected abstract String getCommitHashFromAssignmentRepo(); + String assignmentRepoCommitHash(); @Nullable - protected abstract String getCommitHashFromTestsRepo(); + String testsRepoCommitHash(); @Nullable - public abstract String getBranchNameFromAssignmentRepo(); + String assignmentRepoBranchName(); - public abstract boolean isBuildSuccessful(); + boolean isBuildSuccessful(); - public abstract Double getBuildScore(); + Double buildScore(); /** * Get the commit hash from the build result, the commit hash will be different for submission types or null. @@ -40,22 +38,22 @@ public abstract class AbstractBuildResultNotificationDTO { * @return if the commit hash for the given submission type was found, otherwise empty. */ @Nullable - public String getCommitHash(SubmissionType submissionType) { + default String commitHash(SubmissionType submissionType) { final var isAssignmentSubmission = List.of(SubmissionType.MANUAL, SubmissionType.INSTRUCTOR, SubmissionType.ILLEGAL).contains(submissionType); if (isAssignmentSubmission) { - return getCommitHashFromAssignmentRepo(); + return assignmentRepoCommitHash(); } else if (submissionType.equals(SubmissionType.TEST)) { - return getCommitHashFromTestsRepo(); + return testsRepoCommitHash(); } return null; } - public abstract boolean hasArtifact(); + boolean hasArtifact(); - public abstract boolean hasLogs(); + boolean hasLogs(); - public abstract List extractBuildLogs(); + List extractBuildLogs(); /** * Gets the build jobs that are part of the build result. @@ -63,19 +61,12 @@ else if (submissionType.equals(SubmissionType.TEST)) { * @return list of build jobs. */ @JsonIgnore - public abstract List getBuildJobs(); + List jobs(); /** * Gets the static code analysis reports that are part of the build result. * * @return list of static code analysis reports. */ - public abstract List getStaticCodeAnalysisReports(); - - /** - * Gets the test-wise coverage reports that are part of the build result. - * - * @return list of test-wise coverage reports. - */ - public abstract List getTestwiseCoverageReports(); + List staticCodeAnalysisReports(); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/dto/ConsistencyErrorDTO.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/ConsistencyErrorDTO.java index d200a62e3422..4e4e582cbebe 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/dto/ConsistencyErrorDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/ConsistencyErrorDTO.java @@ -1,7 +1,5 @@ package de.tum.cit.aet.artemis.programming.dto; -import java.util.Objects; - import com.fasterxml.jackson.annotation.JsonInclude; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; @@ -9,63 +7,11 @@ /** * A DTO representing a consistency error */ +// TODO: use a ProgrammingExerciseDTO instead of the whole ProgrammingExercise entity @JsonInclude(JsonInclude.Include.NON_EMPTY) -public class ConsistencyErrorDTO { - - private ProgrammingExercise programmingExercise; - - private ErrorType type; - - public ConsistencyErrorDTO(ProgrammingExercise programmingExercise, ErrorType type) { - this.programmingExercise = programmingExercise; - this.type = type; - } - - public ProgrammingExercise getProgrammingExercise() { - return programmingExercise; - } - - public void setProgrammingExercise(ProgrammingExercise programmingExercise) { - this.programmingExercise = programmingExercise; - } - - public ErrorType getType() { - return type; - } - - public void setType(ErrorType type) { - this.type = type; - } +public record ConsistencyErrorDTO(ProgrammingExercise programmingExercise, ErrorType type) { public enum ErrorType { - VCS_PROJECT_MISSING, TEMPLATE_REPO_MISSING, SOLUTION_REPO_MISSING, AUXILIARY_REPO_MISSING, TEST_REPO_MISSING, TEMPLATE_BUILD_PLAN_MISSING, SOLUTION_BUILD_PLAN_MISSING; - - @Override - public String toString() { - return name(); - } - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - ConsistencyErrorDTO that = (ConsistencyErrorDTO) obj; - return Objects.equals(getProgrammingExercise(), that.getProgrammingExercise()) && getType() == that.getType(); - } - - @Override - public int hashCode() { - return Objects.hash(getProgrammingExercise(), getType()); - } - - @Override - public String toString() { - return "ConsistencyErrorDTO{" + "programmingExercise='" + programmingExercise.getTitle() + "', type='" + type.name() + "'}"; } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/dto/CoverageReportAndSubmissionDateDTO.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/CoverageReportAndSubmissionDateDTO.java deleted file mode 100644 index a5ddfd8550d6..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/dto/CoverageReportAndSubmissionDateDTO.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.tum.cit.aet.artemis.programming.dto; - -import java.time.ZonedDateTime; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageReport; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public record CoverageReportAndSubmissionDateDTO(CoverageReport coverageReport, ZonedDateTime submissionDate) { -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/dto/ProgrammingExerciseGitDiffEntryDTO.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/ProgrammingExerciseGitDiffEntryDTO.java index 6b5f37149cd6..67707c175ab0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/dto/ProgrammingExerciseGitDiffEntryDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/ProgrammingExerciseGitDiffEntryDTO.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseGitDiffEntry; /** * DTO for a git diff report entry. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/dto/ProgrammingExerciseGitDiffReportDTO.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/ProgrammingExerciseGitDiffReportDTO.java index 6f027a90326b..04e32ff039d1 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/dto/ProgrammingExerciseGitDiffReportDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/ProgrammingExerciseGitDiffReportDTO.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseGitDiffReport; /** * DTO for a git diff report. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/dto/TestCaseBaseDTO.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/TestCaseBase.java similarity index 85% rename from src/main/java/de/tum/cit/aet/artemis/programming/dto/TestCaseBaseDTO.java rename to src/main/java/de/tum/cit/aet/artemis/programming/dto/TestCaseBase.java index 267caa9256db..fef892e8fb16 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/dto/TestCaseBaseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/TestCaseBase.java @@ -9,14 +9,14 @@ * Interface for DTOs that represent a test case. */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public interface TestCaseBaseDTO { +public interface TestCaseBase { /** * Gets the name of the test case * * @return the name of the test case */ - String getName(); + String name(); /** * Gets the messages of the test case (typically error messages) @@ -24,5 +24,5 @@ public interface TestCaseBaseDTO { * @return the messages of the test case */ @JsonIgnore - List getTestMessages(); + List testMessages(); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/Action.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/Action.java new file mode 100644 index 000000000000..34f2cfb9537f --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/Action.java @@ -0,0 +1,30 @@ +package de.tum.cit.aet.artemis.programming.dto.aeolus; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Base for the actions that can be defined in a {@link Windfile} + * NOTE: you must create a record that implements this interface to specify actions + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public interface Action { + + Map parameters(); + + Map environment(); + + boolean runAlways(); + + String name(); + + List results(); + + String workdir(); + + String platform(); +} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/AeolusRepository.java similarity index 85% rename from src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusRepository.java rename to src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/AeolusRepository.java index 6793fb84db1d..84dc2e893d88 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/AeolusRepository.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.service.aeolus; +package de.tum.cit.aet.artemis.programming.dto.aeolus; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusResult.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/AeolusResult.java similarity index 83% rename from src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusResult.java rename to src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/AeolusResult.java index 12f319121957..096e98701702 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusResult.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/AeolusResult.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.service.aeolus; +package de.tum.cit.aet.artemis.programming.dto.aeolus; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/DockerConfig.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/DockerConfig.java similarity index 95% rename from src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/DockerConfig.java rename to src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/DockerConfig.java index 5f5161737a6a..ed8cc72283c1 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/DockerConfig.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/DockerConfig.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.service.aeolus; +package de.tum.cit.aet.artemis.programming.dto.aeolus; import java.util.List; diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/PlatformAction.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/PlatformAction.java new file mode 100644 index 000000000000..d2bde6916175 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/PlatformAction.java @@ -0,0 +1,17 @@ +package de.tum.cit.aet.artemis.programming.dto.aeolus; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Represents a CI action that is intended to run only on a specific target, can be used in a {@link Windfile}. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record PlatformAction(String name, Map parameters, Map environment, List results, String workdir, boolean runAlways, + String platform, String kind, String type) implements Action { + +} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/ScriptAction.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/ScriptAction.java similarity index 60% rename from src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/ScriptAction.java rename to src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/ScriptAction.java index 5006e8a850a6..9716b97deac0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/ScriptAction.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/ScriptAction.java @@ -1,4 +1,7 @@ -package de.tum.cit.aet.artemis.programming.service.aeolus; +package de.tum.cit.aet.artemis.programming.dto.aeolus; + +import java.util.List; +import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; @@ -8,18 +11,9 @@ * independent actions but also to run actions on a single target. (e.q. the parsing of the test results that needs to * run on Jenkins but not in LocalCI) */ -// TODO: convert into Record @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) -public class ScriptAction extends Action { - - private String script; - - public String getScript() { - return script; - } +public record ScriptAction(String name, Map parameters, Map environment, List results, String workdir, boolean runAlways, + String platform, String script) implements Action { - public void setScript(String script) { - this.script = script; - } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/Windfile.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/Windfile.java new file mode 100644 index 000000000000..f975a45acc60 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/Windfile.java @@ -0,0 +1,86 @@ +package de.tum.cit.aet.artemis.programming.dto.aeolus; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import de.tum.cit.aet.artemis.programming.service.aeolus.ActionDeserializer; + +/** + * Represents a windfile, the definition file for an aeolus build plan that can then be used to generate a Jenkinsfile. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record Windfile(String api, WindfileMetadata metadata, List actions, Map repositories) { + + public Windfile { + if (actions == null) { + actions = new ArrayList<>(); + } + if (repositories == null) { + repositories = new HashMap<>(); + } + } + + private static final ObjectMapper mapper = new ObjectMapper(); + + /** + * Creates a new windfile based on an existing one with updated metadata. + * + * @param existingWindfile the existing windfile to base the new one on. + * @param metadata the metadata of the newly created windfile. + */ + public Windfile(Windfile existingWindfile, WindfileMetadata metadata) { + this(existingWindfile.api(), metadata, existingWindfile.actions(), existingWindfile.repositories()); + } + + /** + * Creates a new windfile based on an existing one with updated metadata. + * + * @param existingWindfile the existing windfile to base the new one on. + * @param metadata the metadata of the newly created windfile. + * @param repositories the repositories of the newly created windfile. + */ + public Windfile(Windfile existingWindfile, WindfileMetadata metadata, Map repositories) { + this(existingWindfile.api(), metadata, existingWindfile.actions(), repositories); + } + + /** + * Deserializes a windfile from a json string. + * + * @param json the json string to deserialize. + * @return the deserialized windfile. + * @throws JsonProcessingException if the json string is not valid. + */ + public static Windfile deserialize(String json) throws JsonProcessingException { + SimpleModule module = new SimpleModule(); + module.addDeserializer(Action.class, new ActionDeserializer()); + mapper.registerModule(module); + return mapper.readValue(json, Windfile.class); + } + + /** + * Gets the script actions of a windfile. + * + * @return the script actions of a windfile. + */ + public List scriptActions() { + return actions.stream().filter(ScriptAction.class::isInstance).map(ScriptAction.class::cast).toList(); + } + + /** + * Collects the results of all actions of a windfile. + * + * @return the results of all actions of this windfile + */ + public List results() { + return actions.stream().filter(action -> action.results() != null && !action.results().isEmpty()).flatMap(action -> action.results().stream()).toList(); + } +} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/WindfileMetadata.java b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/WindfileMetadata.java similarity index 90% rename from src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/WindfileMetadata.java rename to src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/WindfileMetadata.java index 5e9d1547ddfa..0c5def63abd7 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/WindfileMetadata.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/dto/aeolus/WindfileMetadata.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.service.aeolus; +package de.tum.cit.aet.artemis.programming.dto.aeolus; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ProgrammingExerciseGitDiffReportRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseGitDiffReportRepository.java similarity index 88% rename from src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ProgrammingExerciseGitDiffReportRepository.java rename to src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseGitDiffReportRepository.java index d1c5b9acf8d0..37b6b9fb3522 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ProgrammingExerciseGitDiffReportRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseGitDiffReportRepository.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.repository.hestia; +package de.tum.cit.aet.artemis.programming.repository; import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; @@ -10,7 +10,7 @@ import org.springframework.transaction.annotation.Transactional; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseGitDiffReport; /** * Spring Data JPA repository for the ProgrammingExerciseGitDiffReport entity. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseRepository.java index 73ea3f0cddc3..8641ccd9e48c 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseRepository.java @@ -314,11 +314,9 @@ default ProgrammingExercise findOneByProjectKeyOrThrow(String projectKey, boolea FROM ProgrammingExercise p LEFT JOIN FETCH p.testCases tc LEFT JOIN FETCH p.staticCodeAnalysisCategories - LEFT JOIN FETCH p.exerciseHints LEFT JOIN FETCH p.templateParticipation LEFT JOIN FETCH p.solutionParticipation LEFT JOIN FETCH p.auxiliaryRepositories - LEFT JOIN FETCH tc.solutionEntries LEFT JOIN FETCH p.buildConfig WHERE p.id = :exerciseId """) @@ -330,15 +328,13 @@ Optional findByIdWithEagerTestCasesStaticCodeAnalysisCatego FROM ProgrammingExercise p LEFT JOIN FETCH p.testCases tc LEFT JOIN FETCH p.staticCodeAnalysisCategories - LEFT JOIN FETCH p.exerciseHints LEFT JOIN FETCH p.templateParticipation LEFT JOIN FETCH p.solutionParticipation LEFT JOIN FETCH p.auxiliaryRepositories - LEFT JOIN FETCH tc.solutionEntries LEFT JOIN FETCH p.buildConfig WHERE p.id = :exerciseId """) - Optional findByIdWithEagerBuildConfigTestCasesStaticCodeAnalysisCategoriesHintsAndTemplateAndSolutionParticipationsAndAuxReposAndSolutionEntriesAndBuildConfig( + Optional findByIdWithEagerBuildConfigTestCasesStaticCodeAnalysisCategoriesAndTemplateAndSolutionParticipationsAndAuxReposAndAndBuildConfig( @Param("exerciseId") long exerciseId); @Query(""" @@ -346,11 +342,9 @@ Optional findByIdWithEagerBuildConfigTestCasesStaticCodeAna FROM ProgrammingExercise p LEFT JOIN FETCH p.testCases tc LEFT JOIN FETCH p.staticCodeAnalysisCategories - LEFT JOIN FETCH p.exerciseHints LEFT JOIN FETCH p.templateParticipation LEFT JOIN FETCH p.solutionParticipation LEFT JOIN FETCH p.auxiliaryRepositories - LEFT JOIN FETCH tc.solutionEntries LEFT JOIN FETCH p.buildConfig LEFT JOIN FETCH p.plagiarismDetectionConfig WHERE p.id = :exerciseId @@ -976,7 +970,6 @@ enum ProgrammingExerciseFetchOptions implements FetchOptions { Tasks(ProgrammingExercise_.TASKS), StaticCodeAnalysisCategories(ProgrammingExercise_.STATIC_CODE_ANALYSIS_CATEGORIES), SubmissionPolicy(ProgrammingExercise_.SUBMISSION_POLICY), - ExerciseHints(ProgrammingExercise_.EXERCISE_HINTS), CompetencyLinks(ProgrammingExercise_.COMPETENCY_LINKS), Teams(ProgrammingExercise_.TEAMS), TutorParticipations(ProgrammingExercise_.TUTOR_PARTICIPATIONS), diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ProgrammingExerciseTaskRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseTaskRepository.java similarity index 74% rename from src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ProgrammingExerciseTaskRepository.java rename to src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseTaskRepository.java index 778c0c811374..fb2f47556daf 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ProgrammingExerciseTaskRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseTaskRepository.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.repository.hestia; +package de.tum.cit.aet.artemis.programming.repository; import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; @@ -15,7 +15,7 @@ import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTask; /** * Spring Data repository for the ProgrammingExerciseTask entity. @@ -32,8 +32,8 @@ public interface ProgrammingExerciseTaskRepository extends ArtemisJpaRepository< * @throws EntityNotFoundException If the exercise with exerciseId does not exist */ @NotNull - default List findByExerciseIdWithTestCaseAndSolutionEntriesElseThrow(long exerciseId) throws EntityNotFoundException { - return getArbitraryValueElseThrow(findByExerciseIdWithTestCaseAndSolutionEntries(exerciseId), Long.toString(exerciseId)); + default List findByExerciseIdWithTestCaseElseThrow(long exerciseId) throws EntityNotFoundException { + return getArbitraryValueElseThrow(findByExerciseIdWithTestCase(exerciseId), Long.toString(exerciseId)); } /** @@ -46,11 +46,10 @@ default List findByExerciseIdWithTestCaseAndSolutionEnt SELECT t FROM ProgrammingExerciseTask t LEFT JOIN FETCH t.testCases tc - LEFT JOIN FETCH tc.solutionEntries WHERE t.exercise.id = :exerciseId AND tc.exercise.id = :exerciseId """) - Optional> findByExerciseIdWithTestCaseAndSolutionEntries(@Param("exerciseId") long exerciseId); + Optional> findByExerciseIdWithTestCase(@Param("exerciseId") long exerciseId); /** * Gets all tasks with its test cases for a programming exercise @@ -62,17 +61,7 @@ default List findByExerciseIdWithTestCaseAndSolutionEnt SELECT t FROM ProgrammingExerciseTask t LEFT JOIN FETCH t.testCases tc - LEFT JOIN FETCH tc.solutionEntries WHERE t.exercise.id = :exerciseId """) Set findByExerciseIdWithTestCases(@Param("exerciseId") Long exerciseId); - - @Query(""" - SELECT pt - FROM ProgrammingExerciseTask pt - LEFT JOIN FETCH pt.exerciseHints h - LEFT JOIN FETCH pt.testCases tc - WHERE h.id = :codeHintId - """) - Optional findByCodeHintIdWithTestCases(@Param("codeHintId") Long codeHintId); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseTestCaseRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseTestCaseRepository.java index 0ec309e45e8b..3924a5de856a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseTestCaseRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseTestCaseRepository.java @@ -2,7 +2,6 @@ import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; -import java.util.Optional; import java.util.Set; import org.springframework.context.annotation.Profile; @@ -23,40 +22,6 @@ public interface ProgrammingExerciseTestCaseRepository extends ArtemisJpaReposit Set findByExerciseId(long exerciseId); - default ProgrammingExerciseTestCase findByIdWithExerciseElseThrow(long testCaseId) { - return getValueElseThrow(findByIdWithExercise(testCaseId), testCaseId); - } - - /** - * Returns the test case with the programming exercise - * - * @param testCaseId of the test case - * @return the test case with the programming exercise - */ - @Query(""" - SELECT tc - FROM ProgrammingExerciseTestCase tc - LEFT JOIN FETCH tc.exercise ex - WHERE tc.id = :testCaseId - """) - Optional findByIdWithExercise(@Param("testCaseId") long testCaseId); - - /** - * Returns all test cases with the associated solution entries for a programming exercise - * - * @param exerciseId of the exercise - * @param active status of the test case - * @return all test cases with the associated solution entries - */ - @Query(""" - SELECT DISTINCT tc - FROM ProgrammingExerciseTestCase tc - LEFT JOIN FETCH tc.solutionEntries se - WHERE tc.exercise.id = :exerciseId - AND tc.active = :active - """) - Set findByExerciseIdWithSolutionEntriesAndActive(@Param("exerciseId") long exerciseId, @Param("active") Boolean active); - Set findByExerciseIdAndActive(long exerciseId, Boolean active); /** diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/CodeHintRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/CodeHintRepository.java deleted file mode 100644 index 9c4a03766832..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/CodeHintRepository.java +++ /dev/null @@ -1,50 +0,0 @@ -package de.tum.cit.aet.artemis.programming.repository.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.util.Optional; -import java.util.Set; - -import jakarta.validation.constraints.NotNull; - -import org.springframework.context.annotation.Profile; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; -import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; - -/** - * Spring Data repository for the CodeHint entity. - */ -@Profile(PROFILE_CORE) -@Repository -public interface CodeHintRepository extends ArtemisJpaRepository { - - Set findByExerciseId(Long exerciseId); - - @NotNull - default CodeHint findByIdWithSolutionEntriesElseThrow(long exerciseHintId) throws EntityNotFoundException { - return getValueElseThrow(findByIdWithSolutionEntries(exerciseHintId), exerciseHintId); - } - - @Query(""" - SELECT h - FROM CodeHint h - LEFT JOIN FETCH h.task t - LEFT JOIN FETCH h.solutionEntries tc - WHERE h.id = :codeHintId - """) - Optional findByIdWithSolutionEntries(@Param("codeHintId") Long codeHintId); - - @Query(""" - SELECT h - FROM CodeHint h - LEFT JOIN FETCH h.task t - LEFT JOIN FETCH h.solutionEntries tc - WHERE t.id = :taskId - """) - Set findByTaskIdWithSolutionEntries(@Param("taskId") Long taskId); -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/CoverageFileReportRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/CoverageFileReportRepository.java deleted file mode 100644 index 69bcac01e5ac..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/CoverageFileReportRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.tum.cit.aet.artemis.programming.repository.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; - -import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageFileReport; - -@Profile(PROFILE_CORE) -@Repository -public interface CoverageFileReportRepository extends ArtemisJpaRepository { - -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/CoverageReportRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/CoverageReportRepository.java deleted file mode 100644 index eefc9aeb5848..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/CoverageReportRepository.java +++ /dev/null @@ -1,101 +0,0 @@ -package de.tum.cit.aet.artemis.programming.repository.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; -import static org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.LOAD; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import org.springframework.context.annotation.Profile; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.EntityGraph; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import de.tum.cit.aet.artemis.core.domain.DomainObject; -import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageReport; -import de.tum.cit.aet.artemis.programming.dto.CoverageReportAndSubmissionDateDTO; - -/** - * Spring Data JPA repository for the CoverageReport entity. - */ -@Profile(PROFILE_CORE) -@Repository -public interface CoverageReportRepository extends ArtemisJpaRepository { - - Boolean existsBySubmissionId(Long submissionId); - - @Transactional // ok because of delete - @Modifying - void deleteBySubmissionId(Long submissionId); - - @Query(""" - SELECT new de.tum.cit.aet.artemis.programming.dto.CoverageReportAndSubmissionDateDTO(r, s.submissionDate) - FROM CoverageReport r - JOIN r.submission s - JOIN ProgrammingExercise pe ON s.participation = pe.solutionParticipation - WHERE pe.id = :programmingExerciseId - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) - ORDER BY s.submissionDate DESC - """) - List findCoverageReportsByProgrammingExerciseId(@Param("programmingExerciseId") Long programmingExerciseId, Pageable pageable); - - @EntityGraph(type = LOAD, attributePaths = "submission") - List findCoverageReportsWithSubmissionByIdIn(List ids); - - /** - * Retrieves the latest coverage reports with legal submissions for a specific programming exercise, with pagination support. - * This method avoids in-memory paging by retrieving the coverage report IDs directly from the database. - * - * @param programmingExerciseId the ID of the programming exercise to retrieve the coverage reports for - * @param pageable the pagination information - * @return a list of {@code CoverageReport} with legal submissions, or an empty list if no reports are found - */ - default List getLatestCoverageReportsWithLegalSubmissionsForProgrammingExercise(Long programmingExerciseId, Pageable pageable) { - List ids = findCoverageReportsByProgrammingExerciseId(programmingExerciseId, pageable).stream().map(CoverageReportAndSubmissionDateDTO::coverageReport) - .map(DomainObject::getId).toList(); - if (ids.isEmpty()) { - return Collections.emptyList(); - } - return findCoverageReportsWithSubmissionByIdIn(ids); - } - - @EntityGraph(type = LOAD, attributePaths = { "submission", "fileReports", "fileReports.testwiseCoverageEntries" }) - List findDistinctCoverageReportsWithEagerRelationshipsByIdIn(List ids); - - /** - * Retrieves the latest coverage reports with legal submissions for a specific programming exercise, including eager loading of file reports and entries, with pagination - * support. - * This method avoids in-memory paging by retrieving the coverage report IDs directly from the database. - * - * @param programmingExerciseId the ID of the programming exercise to retrieve the coverage reports for - * @param pageable the pagination information - * @return a list of distinct {@code CoverageReport} with eager relationships, or an empty list if no reports are found - */ - default List getLatestCoverageReportsForLegalSubmissionsForProgrammingExerciseWithEagerFileReportsAndEntries(Long programmingExerciseId, Pageable pageable) { - List ids = findCoverageReportsByProgrammingExerciseId(programmingExerciseId, pageable).stream().map(CoverageReportAndSubmissionDateDTO::coverageReport) - .map(DomainObject::getId).toList(); - if (ids.isEmpty()) { - return Collections.emptyList(); - } - return findDistinctCoverageReportsWithEagerRelationshipsByIdIn(ids); - } - - @Query(""" - SELECT DISTINCT r - FROM CoverageReport r - LEFT JOIN FETCH r.fileReports f - LEFT JOIN FETCH f.testwiseCoverageEntries - WHERE r.id = :coverageReportId - """) - Optional findCoverageReportByIdWithEagerFileReportsAndEntries(@Param("coverageReportId") Long coverageReportId); - - default CoverageReport findCoverageReportByIdWithEagerFileReportsAndEntriesElseThrow(Long coverageReportId) { - return getValueElseThrow(findCoverageReportByIdWithEagerFileReportsAndEntries(coverageReportId), coverageReportId); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ExerciseHintActivationRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ExerciseHintActivationRepository.java deleted file mode 100644 index c827a9b3052b..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ExerciseHintActivationRepository.java +++ /dev/null @@ -1,41 +0,0 @@ -package de.tum.cit.aet.artemis.programming.repository.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.util.Optional; -import java.util.Set; - -import org.springframework.context.annotation.Profile; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHintActivation; - -@Profile(PROFILE_CORE) -@Repository -public interface ExerciseHintActivationRepository extends ArtemisJpaRepository { - - @Query(""" - SELECT hintActivation - FROM ExerciseHintActivation hintActivation - LEFT JOIN FETCH hintActivation.exerciseHint hint - LEFT JOIN FETCH hint.solutionEntries - WHERE hintActivation.exerciseHint.exercise.id = :exerciseId - AND hintActivation.user.id = :userId - """) - Set findByExerciseAndUserWithExerciseHintRelations(@Param("exerciseId") long exerciseId, @Param("userId") long userId); - - @Query(""" - SELECT hintActivation - FROM ExerciseHintActivation hintActivation - WHERE hintActivation.exerciseHint.id = :exerciseHintId - AND hintActivation.user.id = :userId - """) - Optional findByExerciseHintAndUser(@Param("exerciseHintId") long exerciseHintId, @Param("userId") long userId); - - default ExerciseHintActivation findByExerciseHintAndUserElseThrow(long exerciseHintId, long userId) { - return getValueElseThrow(findByExerciseHintAndUser(exerciseHintId, userId)); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ExerciseHintRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ExerciseHintRepository.java deleted file mode 100644 index a5f0eb4942ca..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ExerciseHintRepository.java +++ /dev/null @@ -1,50 +0,0 @@ -package de.tum.cit.aet.artemis.programming.repository.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.util.Optional; -import java.util.Set; - -import jakarta.validation.constraints.NotNull; - -import org.springframework.context.annotation.Profile; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; -import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; - -/** - * Spring Data repository for the ExerciseHint entity. - */ -@Profile(PROFILE_CORE) -@Repository -public interface ExerciseHintRepository extends ArtemisJpaRepository { - - @Query(""" - SELECT h - FROM ExerciseHint h - LEFT JOIN FETCH h.solutionEntries se - WHERE h.id = :hintId - """) - Optional findByIdWithRelations(@Param("hintId") Long hintId); - - @NotNull - default ExerciseHint findByIdWithRelationsElseThrow(long hintId) throws EntityNotFoundException { - return getValueElseThrow(findByIdWithRelations(hintId), hintId); - } - - Set findByExerciseId(Long exerciseId); - - @Query(""" - SELECT h - FROM ExerciseHint h - LEFT JOIN FETCH h.solutionEntries se - WHERE h.exercise.id = :exerciseId - """) - Set findByExerciseIdWithRelations(@Param("exerciseId") Long exerciseId); - - Set findByTaskId(Long taskId); -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ProgrammingExerciseSolutionEntryRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ProgrammingExerciseSolutionEntryRepository.java deleted file mode 100644 index 14a03ed49c0a..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/ProgrammingExerciseSolutionEntryRepository.java +++ /dev/null @@ -1,83 +0,0 @@ -package de.tum.cit.aet.artemis.programming.repository.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.util.Optional; -import java.util.Set; - -import jakarta.validation.constraints.NotNull; - -import org.springframework.context.annotation.Profile; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; -import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; - -/** - * Spring Data repository for the ProgrammingExerciseSolutionEntry entity. - */ -@Profile(PROFILE_CORE) -@Repository -public interface ProgrammingExerciseSolutionEntryRepository extends ArtemisJpaRepository { - - /** - * Gets a solution entry with its test cases and programming exercise - * - * @param entryId The id of the solution entry - * @return The solution entry with the given ID if found - * @throws EntityNotFoundException If no solution entry with the given ID was found - */ - @NotNull - default ProgrammingExerciseSolutionEntry findByIdWithTestCaseAndProgrammingExerciseElseThrow(long entryId) throws EntityNotFoundException { - return getValueElseThrow(findByIdWithTestCaseAndProgrammingExercise(entryId), entryId); - } - - @Query(""" - SELECT se - FROM ProgrammingExerciseSolutionEntry se - LEFT JOIN FETCH se.testCase tc - LEFT JOIN FETCH tc.exercise pe - WHERE pe.id = :exerciseId - """) - Set findByExerciseIdWithTestCases(@Param("exerciseId") long exerciseId); - - /** - * Gets a solution entry with its test cases and programming exercise - * - * @param entryId The id of the solution entry - * @return The solution entry with the given ID - */ - @Query(""" - SELECT se - FROM ProgrammingExerciseSolutionEntry se - LEFT JOIN FETCH se.testCase tc - LEFT JOIN FETCH tc.exercise pe - WHERE se.id = :entryId - """) - Optional findByIdWithTestCaseAndProgrammingExercise(@Param("entryId") long entryId); - - @Query(""" - SELECT h.solutionEntries - FROM CodeHint h - WHERE h.id = :codeHintId - """) - Set findByCodeHintId(@Param("codeHintId") Long codeHintId); - - @Query(""" - SELECT t.solutionEntries - FROM ProgrammingExerciseTestCase t - WHERE t.id = :testCaseId - """) - Set findByTestCaseId(@Param("testCaseId") Long testCaseId); - - @Query(""" - SELECT se - FROM ProgrammingExerciseSolutionEntry se - LEFT JOIN FETCH se.codeHint - WHERE se.testCase.id = :testCaseId - """) - Set findByTestCaseIdWithCodeHint(@Param("testCaseId") Long testCaseId); -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/TestwiseCoverageReportEntryRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/TestwiseCoverageReportEntryRepository.java deleted file mode 100644 index 253adf77f45a..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/hestia/TestwiseCoverageReportEntryRepository.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.tum.cit.aet.artemis.programming.repository.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; - -import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; -import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; - -/** - * Spring Data JPA repository for the TestwiseCoverageReportEntry entity. - */ -@Profile(PROFILE_CORE) -@Repository -public interface TestwiseCoverageReportEntryRepository extends ArtemisJpaRepository { - -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildScriptProviderService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildScriptProviderService.java index 487c92b3c21d..2d6e9bff2786 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildScriptProviderService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/BuildScriptProviderService.java @@ -103,13 +103,11 @@ public String getCachedScript(String key) { * @param projectType the project type for which the template file should be returned. If omitted, a default depending on the language will be used. * @param staticAnalysis whether the static analysis template should be used * @param sequentialRuns whether the sequential runs template should be used - * @param testCoverage whether the test coverage template should be used * @return the requested template as a bash script * @throws IOException if the file does not exist */ - public String getScriptFor(ProgrammingLanguage programmingLanguage, Optional projectType, Boolean staticAnalysis, Boolean sequentialRuns, Boolean testCoverage) - throws IOException { - String templateFileName = buildTemplateName(projectType, staticAnalysis, sequentialRuns, testCoverage, "sh"); + public String getScriptFor(ProgrammingLanguage programmingLanguage, Optional projectType, Boolean staticAnalysis, Boolean sequentialRuns) throws IOException { + String templateFileName = buildTemplateName(projectType, staticAnalysis, sequentialRuns, "sh"); String uniqueKey = programmingLanguage.name().toLowerCase() + "_" + templateFileName; if (scriptCache.containsKey(uniqueKey)) { log.debug("Returning cached script for {}", uniqueKey); @@ -139,7 +137,7 @@ public String getScriptFor(ProgrammingExercise exercise) { try { ProgrammingExerciseBuildConfig buildConfig = exercise.getBuildConfig(); return getScriptFor(exercise.getProgrammingLanguage(), Optional.ofNullable(exercise.getProjectType()), exercise.isStaticCodeAnalysisEnabled(), - buildConfig.hasSequentialTestRuns(), buildConfig.isTestwiseCoverageEnabled()); + buildConfig.hasSequentialTestRuns()); } catch (IOException e) { log.error("Failed to provide build script for programming exercise " + exercise.getId(), e); @@ -153,11 +151,10 @@ public String getScriptFor(ProgrammingExercise exercise) { * @param projectType The project type for which the template file should be returned. If omitted, a default depending on the language will be used. * @param staticAnalysis whether the static analysis template should be used * @param sequentialRuns whether the sequential runs template should be used - * @param testCoverage whether the test coverage template should be used * @param fileExtension the file extension of the template file * @return The filename of the requested configuration */ - public String buildTemplateName(Optional projectType, Boolean staticAnalysis, Boolean sequentialRuns, Boolean testCoverage, String fileExtension) { + public String buildTemplateName(Optional projectType, Boolean staticAnalysis, Boolean sequentialRuns, String fileExtension) { List fileNameComponents = new ArrayList<>(); if (ProjectType.MAVEN_BLACKBOX.equals(projectType.orElse(null))) { @@ -173,9 +170,6 @@ public String buildTemplateName(Optional projectType, Boolean stati if (sequentialRuns) { fileNameComponents.add("sequential"); } - if (testCoverage) { - fileNameComponents.add("coverage"); - } return String.join("_", fileNameComponents) + "." + fileExtension; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/CommitHistoryService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/CommitHistoryService.java index 9ee663c55c96..bf4f8496cd93 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/CommitHistoryService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/CommitHistoryService.java @@ -19,11 +19,10 @@ import org.springframework.stereotype.Service; import de.tum.cit.aet.artemis.core.service.ProfileService; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseGitDiffEntry; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseGitDiffReport; import de.tum.cit.aet.artemis.programming.domain.Repository; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; -import de.tum.cit.aet.artemis.programming.web.GitDiffReportParserService; @Profile(PROFILE_CORE) @Service diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/GenericBuildScriptGenerationService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/GenericBuildScriptGenerationService.java index adabad775f23..5d8008d0c833 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/GenericBuildScriptGenerationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/GenericBuildScriptGenerationService.java @@ -34,7 +34,7 @@ public String getScript(ProgrammingExercise programmingExercise) { try { ProgrammingExerciseBuildConfig buildConfig = programmingExercise.getBuildConfig(); return buildScriptProviderService.getScriptFor(programmingExercise.getProgrammingLanguage(), Optional.ofNullable(programmingExercise.getProjectType()), - programmingExercise.isStaticCodeAnalysisEnabled(), buildConfig.hasSequentialTestRuns(), buildConfig.isTestwiseCoverageEnabled()); + programmingExercise.isStaticCodeAnalysisEnabled(), buildConfig.hasSequentialTestRuns()); } catch (IOException e) { log.error("Failed to generate build script for programming exercise " + programmingExercise.getId(), e); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/GitDiffReportParserService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/GitDiffReportParserService.java similarity index 98% rename from src/main/java/de/tum/cit/aet/artemis/programming/web/GitDiffReportParserService.java rename to src/main/java/de/tum/cit/aet/artemis/programming/service/GitDiffReportParserService.java index 37042b8cdb06..f66d85d65544 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/GitDiffReportParserService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/GitDiffReportParserService.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.web; +package de.tum.cit.aet.artemis.programming.service; import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; @@ -13,7 +13,7 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseGitDiffEntry; @Profile(PROFILE_CORE) @Service diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java index 20d98af84bc8..76a140eb2147 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java @@ -74,7 +74,6 @@ import de.tum.cit.aet.artemis.programming.repository.AuxiliaryRepositoryRepository; import de.tum.cit.aet.artemis.programming.repository.BuildPlanRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; /** * Service for exporting programming exercises. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseFeedbackCreationService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseFeedbackCreationService.java index d22c13ebe3d8..a75efa7bf1c2 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseFeedbackCreationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseFeedbackCreationService.java @@ -37,18 +37,17 @@ import de.tum.cit.aet.artemis.core.config.StaticCodeAnalysisConfigurer; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCaseType; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.StaticCodeAnalysisCategory; import de.tum.cit.aet.artemis.programming.domain.StaticCodeAnalysisDefaultCategory; import de.tum.cit.aet.artemis.programming.domain.StaticCodeAnalysisTool; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTestCaseType; -import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO; +import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification; import de.tum.cit.aet.artemis.programming.dto.StaticCodeAnalysisIssue; import de.tum.cit.aet.artemis.programming.dto.StaticCodeAnalysisReportDTO; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; import de.tum.cit.aet.artemis.programming.repository.StaticCodeAnalysisCategoryRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; /** * Service for creating feedback for programming exercises. @@ -278,7 +277,7 @@ else if (!testMessages.isEmpty()) { * @param buildResult from which to extract the test cases. * @param exercise the programming exercise for which the test cases should be extracted from the new result */ - public void extractTestCasesFromResultAndBroadcastUpdates(AbstractBuildResultNotificationDTO buildResult, ProgrammingExercise exercise) { + public void extractTestCasesFromResultAndBroadcastUpdates(BuildResultNotification buildResult, ProgrammingExercise exercise) { boolean haveTestCasesChanged = generateTestCasesFromBuildResult(buildResult, exercise); if (haveTestCasesChanged) { // Notify the client about the updated testCases @@ -296,7 +295,7 @@ public void extractTestCasesFromResultAndBroadcastUpdates(AbstractBuildResultNot * @param exercise programming exercise. * @return Returns true if the test cases have changed, false if they haven't. */ - public boolean generateTestCasesFromBuildResult(AbstractBuildResultNotificationDTO buildResult, ProgrammingExercise exercise) { + public boolean generateTestCasesFromBuildResult(BuildResultNotification buildResult, ProgrammingExercise exercise) { Set existingTestCases = testCaseRepository.findByExerciseId(exercise.getId()); // Do not generate test cases for static code analysis feedback Set testCasesFromFeedbacks = getTestCasesFromBuildResult(buildResult, exercise); @@ -375,12 +374,12 @@ public void setTestCaseType(Set testCases, Programm }); } - private Set getTestCasesFromBuildResult(AbstractBuildResultNotificationDTO buildResult, ProgrammingExercise exercise) { + private Set getTestCasesFromBuildResult(BuildResultNotification buildResult, ProgrammingExercise exercise) { Visibility defaultVisibility = exercise.getDefaultTestCaseVisibility(); - return buildResult.getBuildJobs().stream().flatMap(job -> Stream.concat(job.getFailedTests().stream(), job.getSuccessfulTests().stream())) + return buildResult.jobs().stream().flatMap(job -> Stream.concat(job.failedTests().stream(), job.successfulTests().stream())) // we use default values for weight, bonus multiplier and bonus points - .map(testCase -> new ProgrammingExerciseTestCase().testName(testCase.getName()).weight(1.0).bonusMultiplier(1.0).bonusPoints(0.0).exercise(exercise).active(true) + .map(testCase -> new ProgrammingExerciseTestCase().testName(testCase.name()).weight(1.0).bonusMultiplier(1.0).bonusPoints(0.0).exercise(exercise).active(true) .visibility(defaultVisibility)) .collect(Collectors.toSet()); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/ProgrammingExerciseGitDiffReportService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseGitDiffReportService.java similarity index 97% rename from src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/ProgrammingExerciseGitDiffReportService.java rename to src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseGitDiffReportService.java index 6950e1216df5..785d200ad349 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/ProgrammingExerciseGitDiffReportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseGitDiffReportService.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.service.hestia; +package de.tum.cit.aet.artemis.programming.service; import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; @@ -24,21 +24,19 @@ import de.tum.cit.aet.artemis.core.exception.InternalServerErrorException; import de.tum.cit.aet.artemis.core.service.FileService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseGitDiffEntry; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseGitDiffReport; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; import de.tum.cit.aet.artemis.programming.domain.Repository; import de.tum.cit.aet.artemis.programming.domain.SolutionProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.TemplateProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseGitDiffReportRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingSubmissionRepository; import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; import de.tum.cit.aet.artemis.programming.repository.TemplateProgrammingExerciseParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseGitDiffReportRepository; -import de.tum.cit.aet.artemis.programming.service.GitService; -import de.tum.cit.aet.artemis.programming.web.GitDiffReportParserService; /** * The service handling ProgrammingExerciseGitDiffReport and their ProgrammingExerciseGitDiffEntries. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseGradingService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseGradingService.java index 7738b6615b2f..1aff8fca66f0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseGradingService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseGradingService.java @@ -61,7 +61,7 @@ import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.LockRepositoryPolicy; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.SubmissionPenaltyPolicy; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.SubmissionPolicy; -import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO; +import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification; import de.tum.cit.aet.artemis.programming.dto.ProgrammingExerciseGradingStatisticsDTO; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; @@ -218,9 +218,8 @@ public Result processNewProgrammingExerciseResult(@NotNull ProgrammingExercisePa * @param buildResult The build result received from the CI system. * @throws IllegalArgumentException Thrown if the result does not belong to the default branch of the exercise. */ - private void checkCorrectBranchElseThrow(final ProgrammingExerciseParticipation participation, final AbstractBuildResultNotificationDTO buildResult) - throws IllegalArgumentException { - var branchName = buildResult.getBranchNameFromAssignmentRepo(); + private void checkCorrectBranchElseThrow(final ProgrammingExerciseParticipation participation, final BuildResultNotification buildResult) throws IllegalArgumentException { + var branchName = buildResult.assignmentRepoBranchName(); // If the branch is not present, it might be because the assignment repo did not change because only the test repo was changed if (!ObjectUtils.isEmpty(branchName)) { String participationDefaultBranch = null; @@ -243,8 +242,8 @@ private void checkCorrectBranchElseThrow(final ProgrammingExerciseParticipation * * @param buildResult The build result received from the CI system. */ - private void checkHasCommitHashElseThrow(final AbstractBuildResultNotificationDTO buildResult) { - if (StringUtils.isEmpty(buildResult.getCommitHash(SubmissionType.MANUAL))) { + private void checkHasCommitHashElseThrow(final BuildResultNotification buildResult) { + if (StringUtils.isEmpty(buildResult.commitHash(SubmissionType.MANUAL))) { throw new IllegalArgumentException("The provided result does not specify the assignment commit hash. The result will not get processed."); } } @@ -256,28 +255,28 @@ private void checkHasCommitHashElseThrow(final AbstractBuildResultNotificationDT * @param buildResult The build result * @return The submission or empty if no submissions exist */ - protected Optional getSubmissionForBuildResult(Long participationId, AbstractBuildResultNotificationDTO buildResult) { + protected Optional getSubmissionForBuildResult(Long participationId, BuildResultNotification buildResult) { var submissions = programmingSubmissionRepository.findAllByParticipationIdWithResults(participationId); if (submissions.isEmpty()) { return Optional.empty(); } return submissions.stream().filter(theSubmission -> { - var commitHash = buildResult.getCommitHash(theSubmission.getType()); + var commitHash = buildResult.commitHash(theSubmission.getType()); return !ObjectUtils.isEmpty(commitHash) && commitHash.equals(theSubmission.getCommitHash()); }).max(Comparator.naturalOrder()); } @NotNull - protected ProgrammingSubmission createAndSaveFallbackSubmission(ProgrammingExerciseParticipation participation, AbstractBuildResultNotificationDTO buildResult) { - final var commitHash = buildResult.getCommitHash(SubmissionType.MANUAL); + protected ProgrammingSubmission createAndSaveFallbackSubmission(ProgrammingExerciseParticipation participation, BuildResultNotification buildResult) { + final var commitHash = buildResult.commitHash(SubmissionType.MANUAL); if (ObjectUtils.isEmpty(commitHash)) { log.error("Could not find commit hash for participation {}, build plan {}", participation.getId(), participation.getBuildPlanId()); } log.warn("Could not find pending ProgrammingSubmission for Commit Hash {} (Participation {}, Build Plan {}). Will create a new one subsequently...", commitHash, participation.getId(), participation.getBuildPlanId()); // We always take the build run date as the fallback solution - ZonedDateTime submissionDate = buildResult.getBuildRunDate(); + ZonedDateTime submissionDate = buildResult.buildRunDate(); if (!ObjectUtils.isEmpty(commitHash)) { try { // Try to get the actual date, the push might be 10s - 3min earlier, depending on how long the build takes. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportBasicService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportBasicService.java index 8f5c1b427390..92bddf8f75d4 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportBasicService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportBasicService.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import org.springframework.context.annotation.Profile; @@ -22,34 +23,24 @@ import de.tum.cit.aet.artemis.programming.domain.AuxiliaryRepository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseBuildConfig; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.RepositoryType; import de.tum.cit.aet.artemis.programming.domain.StaticCodeAnalysisCategory; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.SubmissionPolicy; import de.tum.cit.aet.artemis.programming.repository.AuxiliaryRepositoryRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTaskRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; import de.tum.cit.aet.artemis.programming.repository.StaticCodeAnalysisCategoryRepository; import de.tum.cit.aet.artemis.programming.repository.SubmissionPolicyRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.ExerciseHintService; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; import de.tum.cit.aet.artemis.programming.service.vcs.VersionControlService; @Profile(PROFILE_CORE) @Service public class ProgrammingExerciseImportBasicService { - private final ExerciseHintService exerciseHintService; - - private final ExerciseHintRepository exerciseHintRepository; - private final Optional versionControlService; private final ProgrammingExerciseParticipationService programmingExerciseParticipationService; @@ -74,22 +65,17 @@ public class ProgrammingExerciseImportBasicService { private final ProgrammingExerciseTaskService programmingExerciseTaskService; - private final ProgrammingExerciseSolutionEntryRepository solutionEntryRepository; - private final ChannelService channelService; private final ExerciseService exerciseService; - public ProgrammingExerciseImportBasicService(ExerciseHintService exerciseHintService, ExerciseHintRepository exerciseHintRepository, - Optional versionControlService, ProgrammingExerciseParticipationService programmingExerciseParticipationService, - ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository, StaticCodeAnalysisCategoryRepository staticCodeAnalysisCategoryRepository, - ProgrammingExerciseRepository programmingExerciseRepository, ProgrammingExerciseService programmingExerciseService, StaticCodeAnalysisService staticCodeAnalysisService, - AuxiliaryRepositoryRepository auxiliaryRepositoryRepository, SubmissionPolicyRepository submissionPolicyRepository, - ProgrammingExerciseTaskRepository programmingExerciseTaskRepository, ProgrammingExerciseTaskService programmingExerciseTaskService, - ProgrammingExerciseSolutionEntryRepository solutionEntryRepository, ChannelService channelService, + public ProgrammingExerciseImportBasicService(Optional versionControlService, + ProgrammingExerciseParticipationService programmingExerciseParticipationService, ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository, + StaticCodeAnalysisCategoryRepository staticCodeAnalysisCategoryRepository, ProgrammingExerciseRepository programmingExerciseRepository, + ProgrammingExerciseService programmingExerciseService, StaticCodeAnalysisService staticCodeAnalysisService, AuxiliaryRepositoryRepository auxiliaryRepositoryRepository, + SubmissionPolicyRepository submissionPolicyRepository, ProgrammingExerciseTaskRepository programmingExerciseTaskRepository, + ProgrammingExerciseTaskService programmingExerciseTaskService, ChannelService channelService, ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository, ExerciseService exerciseService) { - this.exerciseHintService = exerciseHintService; - this.exerciseHintRepository = exerciseHintRepository; this.versionControlService = versionControlService; this.programmingExerciseParticipationService = programmingExerciseParticipationService; this.programmingExerciseTestCaseRepository = programmingExerciseTestCaseRepository; @@ -101,7 +87,6 @@ public ProgrammingExerciseImportBasicService(ExerciseHintService exerciseHintSer this.submissionPolicyRepository = submissionPolicyRepository; this.programmingExerciseTaskRepository = programmingExerciseTaskRepository; this.programmingExerciseTaskService = programmingExerciseTaskService; - this.solutionEntryRepository = solutionEntryRepository; this.channelService = channelService; this.programmingExerciseBuildConfigRepository = programmingExerciseBuildConfigRepository; this.exerciseService = exerciseService; @@ -143,20 +128,16 @@ public ProgrammingExercise importProgrammingExerciseBasis(final ProgrammingExerc } // Hints, tasks, test cases and static code analysis categories - final Map newHintIdByOldId = exerciseHintService.copyExerciseHints(originalProgrammingExercise, newProgrammingExercise); - newProgrammingExercise.setBuildConfig(programmingExerciseBuildConfigRepository.save(newProgrammingExercise.getBuildConfig())); final ProgrammingExercise importedExercise = exerciseService.saveWithCompetencyLinks(newProgrammingExercise, programmingExerciseRepository::save); final Map newTestCaseIdByOldId = importTestCases(originalProgrammingExercise, importedExercise); - final Map newTaskIdByOldId = importTasks(originalProgrammingExercise, importedExercise, newTestCaseIdByOldId); - updateTaskExerciseHintReferences(originalProgrammingExercise, importedExercise, newTaskIdByOldId, newHintIdByOldId); + importTasks(originalProgrammingExercise, importedExercise, newTestCaseIdByOldId); // Set up new exercise submission policy before the solution entries are imported importSubmissionPolicy(importedExercise); // Having the submission policy in place prevents errors - importSolutionEntries(originalProgrammingExercise, importedExercise, newTestCaseIdByOldId, newHintIdByOldId); // Use the template problem statement (with ids) as a new basis (You cannot edit the problem statement while importing) // Then replace the old test ids by the newly created ones. @@ -284,33 +265,62 @@ private Map importTestCases(final ProgrammingExercise templateExerci } /** - * Copies tasks from one exercise to another. Because the tasks from the template exercise references its test cases, the - * references between tasks and test cases also need to be changed. + * Imports tasks from a template exercise to a new exercise. The tasks will get new IDs, thus being saved as a new entity. + * The remaining contents stay the same, especially the test cases. * - * @param templateExercise The template exercise which tasks should be copied - * @param targetExercise The new exercise to which all tasks should get copied to - * @param newTestCaseIdByOldId A map with the old test case id as a key and the new test case id as a value - * @return A map with the old task id as a key and the new task id as value + * @param sourceExercise The template exercise which tasks should get copied + * @param targetExercise The new exercise to which all tasks should get copied to + * @param testCaseIdMapping A map with the old test case id as a key and the new test case id as a value */ - private Map importTasks(final ProgrammingExercise templateExercise, final ProgrammingExercise targetExercise, Map newTestCaseIdByOldId) { - Map newIdByOldId = new HashMap<>(); - targetExercise.setTasks(templateExercise.getTasks().stream().map(task -> { - final var copy = new ProgrammingExerciseTask(); - - // copy everything except for the referenced exercise - copy.setTaskName(task.getTaskName()); - // change reference to newly imported test cases from the target exercise - copy.setTestCases(task.getTestCases().stream().map(testCase -> { - Long oldTestCaseId = testCase.getId(); - Long newTestCaseId = newTestCaseIdByOldId.get(oldTestCaseId); - return targetExercise.getTestCases().stream().filter(newTestCase -> Objects.equals(newTestCaseId, newTestCase.getId())).findFirst().orElseThrow(); - }).collect(Collectors.toSet())); - copy.setExercise(targetExercise); - programmingExerciseTaskRepository.save(copy); - newIdByOldId.put(task.getId(), copy.getId()); - return copy; - }).collect(Collectors.toCollection(ArrayList::new))); - return newIdByOldId; + private void importTasks(final ProgrammingExercise sourceExercise, final ProgrammingExercise targetExercise, Map testCaseIdMapping) { + // Map the tasks from the template exercise to new tasks in the target exercise + List newTasks = sourceExercise.getTasks().stream().map(templateTask -> createTaskCopy(templateTask, targetExercise, testCaseIdMapping)).toList(); + + // Set the new tasks to the target exercise + targetExercise.setTasks(new ArrayList<>(newTasks)); + } + + /** + * Creates a copy of a task from a template exercise and links it to the target exercise. The test cases of the task + * are also copied and linked to the new task. + * + * @param sourceTask The template task which should be copied + * @param targetExercise The new exercise to which the task should be linked + * @param testCaseIdMapping A map with the old test case id as a key and the new test case id as a value + * @return The new task + */ + private ProgrammingExerciseTask createTaskCopy(ProgrammingExerciseTask sourceTask, ProgrammingExercise targetExercise, Map testCaseIdMapping) { + ProgrammingExerciseTask copiedTask = new ProgrammingExerciseTask(); + + // Copy task properties + copiedTask.setTaskName(sourceTask.getTaskName()); + + // Map and set new test cases + Set mappedTestCases = sourceTask.getTestCases().stream().map(testCase -> findMappedTestCase(testCase, targetExercise, testCaseIdMapping)) + .collect(Collectors.toSet()); + copiedTask.setTestCases(mappedTestCases); + + // Link the task to the target exercise + copiedTask.setExercise(targetExercise); + + // Persist the new task + programmingExerciseTaskRepository.save(copiedTask); + return copiedTask; + } + + /** + * Finds a test case in the target exercise that corresponds to a test case in the template exercise. + * + * @param existingTestCase The test case from the template exercise + * @param targetExercise The new exercise to which the test case should be linked + * @param testCaseIdMapping A map with the old test case id as a key and the new test case id as a value + * @return The test case in the target exercise + */ + private ProgrammingExerciseTestCase findMappedTestCase(ProgrammingExerciseTestCase existingTestCase, ProgrammingExercise targetExercise, Map testCaseIdMapping) { + Long newTestCaseId = testCaseIdMapping.get(existingTestCase.getId()); + + return targetExercise.getTestCases().stream().filter(newTestCase -> Objects.equals(newTestCaseId, newTestCase.getId())).findFirst() + .orElseThrow(() -> new IllegalArgumentException("Test case not found for ID: " + newTestCaseId)); } /** @@ -380,68 +390,4 @@ else if (newExercise.isCourseExercise() && newExercise.getPlagiarismDetectionCon newExercise.setPlagiarismDetectionConfig(null); } } - - /** - * Updates the newly imported exercise hints to reference the newly imported tasks they belong to. - * - * @param templateExercise The template exercise which tasks should be copied - * @param targetExercise The new exercise to which all tasks should get copied to - * @param newTaskIdByOldId A map with the old task id as a key and the new task id as a value - * @param newHintIdByOldId A map with the old hint id as a key and the new hint id as a value - */ - private void updateTaskExerciseHintReferences(final ProgrammingExercise templateExercise, final ProgrammingExercise targetExercise, Map newTaskIdByOldId, - Map newHintIdByOldId) { - templateExercise.getExerciseHints().forEach(templateExerciseHint -> { - var templateTask = templateExerciseHint.getProgrammingExerciseTask(); - if (templateTask == null) { - return; - } - var targetTask = targetExercise.getTasks().stream().filter(newTask -> Objects.equals(newTask.getId(), newTaskIdByOldId.get(templateTask.getId()))).findAny() - .orElseThrow(); - var targetExerciseHint = targetExercise.getExerciseHints().stream() - .filter(newHint -> Objects.equals(newHint.getId(), newHintIdByOldId.get(templateExerciseHint.getId()))).findAny().orElseThrow(); - - targetExerciseHint.setProgrammingExerciseTask(targetTask); - exerciseHintRepository.save(targetExerciseHint); - targetTask.getExerciseHints().add(targetExerciseHint); - }); - } - - /** - * Copies solution entries from one exercise to another. Because the solution entries from the template exercise - * references its test cases and code hint, the references between them also need to be changed. - * - * @param templateExercise The template exercise which tasks should be copied - * @param targetExercise The new exercise to which all tasks should get copied to - * @param newTestCaseIdByOldId A map with the old test case id as a key and the new test case id as a value - * @param newHintIdByOldId A map with the old hint id as a key and the new hint id as a value - */ - private void importSolutionEntries(final ProgrammingExercise templateExercise, final ProgrammingExercise targetExercise, Map newTestCaseIdByOldId, - Map newHintIdByOldId) { - templateExercise.getTestCases().forEach(testCase -> { - var newSolutionEntries = solutionEntryRepository.findByTestCaseIdWithCodeHint(testCase.getId()).stream().map(solutionEntry -> { - Long newTestCaseId = newTestCaseIdByOldId.get(testCase.getId()); - var targetTestCase = targetExercise.getTestCases().stream().filter(newTestCase -> Objects.equals(newTestCaseId, newTestCase.getId())).findFirst().orElseThrow(); - - CodeHint codeHint = null; - if (solutionEntry.getCodeHint() != null) { - Long newHintId = newHintIdByOldId.get(solutionEntry.getCodeHint().getId()); - codeHint = (CodeHint) targetExercise.getExerciseHints().stream().filter(newHint -> Objects.equals(newHintId, newHint.getId())).findFirst().orElseThrow(); - } - var copy = new ProgrammingExerciseSolutionEntry(); - copy.setCode(solutionEntry.getCode()); - copy.setPreviousCode(solutionEntry.getPreviousCode()); - copy.setLine(solutionEntry.getLine()); - copy.setPreviousLine(solutionEntry.getPreviousLine()); - copy.setTestCase(targetTestCase); - targetTestCase.getSolutionEntries().add(copy); - copy.setCodeHint(codeHint); - if (codeHint != null) { - codeHint.getSolutionEntries().add(copy); - } - return copy; - }).collect(Collectors.toSet()); - solutionEntryRepository.saveAll(newSolutionEntries); - }); - } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportService.java index 710837ac4fb4..0c308cc80c0a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportService.java @@ -35,7 +35,6 @@ import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationTriggerService; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; import de.tum.cit.aet.artemis.programming.service.vcs.VersionControlService; @Profile(PROFILE_CORE) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseRepositoryService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseRepositoryService.java index 43523ec566f1..ffbf6aa37f0a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseRepositoryService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseRepositoryService.java @@ -404,8 +404,6 @@ private void setupJVMTestTemplateAndPush(final RepositoryResources resources, fi final Map sectionsMap = new HashMap<>(); // Keep or delete static code analysis configuration in the build configuration file sectionsMap.put("static-code-analysis", Boolean.TRUE.equals(programmingExercise.isStaticCodeAnalysisEnabled())); - // Keep or delete testwise coverage configuration in the build file - sectionsMap.put("record-testwise-coverage", Boolean.TRUE.equals(programmingExercise.getBuildConfig().isTestwiseCoverageEnabled())); if (programmingExercise.getBuildConfig().hasSequentialTestRuns()) { setupTestTemplateSequentialTestRuns(resources, templatePath, projectTemplatePath, projectType, sectionsMap); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java index d85af7904c4c..d03c14a5cbc5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java @@ -17,7 +17,6 @@ import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -26,7 +25,6 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import jakarta.annotation.Nullable; @@ -70,7 +68,7 @@ import de.tum.cit.aet.artemis.programming.domain.AuxiliaryRepository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseBuildConfig; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.Repository; @@ -78,23 +76,19 @@ import de.tum.cit.aet.artemis.programming.domain.SolutionProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.TemplateProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; import de.tum.cit.aet.artemis.programming.repository.AuxiliaryRepositoryRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseGitDiffReportRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseStudentParticipationRepository; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTaskRepository; import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; import de.tum.cit.aet.artemis.programming.repository.TemplateProgrammingExerciseParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseGitDiffReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusTemplateService; -import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile; import de.tum.cit.aet.artemis.programming.service.ci.CIPermission; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationTriggerService; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; import de.tum.cit.aet.artemis.programming.service.structureoraclegenerator.OracleGenerator; import de.tum.cit.aet.artemis.programming.service.vcs.VersionControlService; @@ -163,8 +157,6 @@ public class ProgrammingExerciseService { private final ProgrammingExerciseTaskRepository programmingExerciseTaskRepository; - private final ProgrammingExerciseSolutionEntryRepository programmingExerciseSolutionEntryRepository; - private final ProgrammingExerciseTaskService programmingExerciseTaskService; private final ProgrammingExerciseGitDiffReportRepository programmingExerciseGitDiffReportRepository; @@ -206,11 +198,11 @@ public ProgrammingExerciseService(ProgrammingExerciseRepository programmingExerc ParticipationRepository participationRepository, ResultRepository resultRepository, UserRepository userRepository, GroupNotificationScheduleService groupNotificationScheduleService, InstanceMessageSendService instanceMessageSendService, AuxiliaryRepositoryRepository auxiliaryRepositoryRepository, ProgrammingExerciseTaskRepository programmingExerciseTaskRepository, - ProgrammingExerciseSolutionEntryRepository programmingExerciseSolutionEntryRepository, ProgrammingExerciseTaskService programmingExerciseTaskService, - ProgrammingExerciseGitDiffReportRepository programmingExerciseGitDiffReportRepository, ExerciseSpecificationService exerciseSpecificationService, - ProgrammingExerciseRepositoryService programmingExerciseRepositoryService, AuxiliaryRepositoryService auxiliaryRepositoryService, - SubmissionPolicyService submissionPolicyService, Optional programmingLanguageFeatureService, ChannelService channelService, - ProgrammingSubmissionService programmingSubmissionService, Optional irisSettingsService, Optional aeolusTemplateService, + ProgrammingExerciseTaskService programmingExerciseTaskService, ProgrammingExerciseGitDiffReportRepository programmingExerciseGitDiffReportRepository, + ExerciseSpecificationService exerciseSpecificationService, ProgrammingExerciseRepositoryService programmingExerciseRepositoryService, + AuxiliaryRepositoryService auxiliaryRepositoryService, SubmissionPolicyService submissionPolicyService, + Optional programmingLanguageFeatureService, ChannelService channelService, ProgrammingSubmissionService programmingSubmissionService, + Optional irisSettingsService, Optional aeolusTemplateService, Optional buildScriptGenerationService, ProgrammingExerciseStudentParticipationRepository programmingExerciseStudentParticipationRepository, ProfileService profileService, ExerciseService exerciseService, ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository, CompetencyProgressApi competencyProgressApi, @@ -230,7 +222,6 @@ public ProgrammingExerciseService(ProgrammingExerciseRepository programmingExerc this.instanceMessageSendService = instanceMessageSendService; this.auxiliaryRepositoryRepository = auxiliaryRepositoryRepository; this.programmingExerciseTaskRepository = programmingExerciseTaskRepository; - this.programmingExerciseSolutionEntryRepository = programmingExerciseSolutionEntryRepository; this.programmingExerciseTaskService = programmingExerciseTaskService; this.programmingExerciseGitDiffReportRepository = programmingExerciseGitDiffReportRepository; this.exerciseSpecificationService = exerciseSpecificationService; @@ -405,11 +396,6 @@ public void validateNewProgrammingExerciseSettings(ProgrammingExercise programmi throw new BadRequestAlertException("Checkout solution repository is not supported for this programming language", "Exercise", "checkoutSolutionRepositoryNotSupported"); } - // Check if testwise coverage analysis is enabled - if (Boolean.TRUE.equals(buildConfig.isTestwiseCoverageEnabled()) && !programmingLanguageFeature.testwiseCoverageAnalysisSupported()) { - throw new BadRequestAlertException("Testwise coverage analysis is not supported for this language", "Exercise", "testwiseCoverageAnalysisNotSupported"); - } - programmingExerciseRepository.validateCourseSettings(programmingExercise, course); validateStaticCodeAnalysisSettings(programmingExercise); @@ -1033,12 +1019,9 @@ public boolean preCheckProjectExistsOnVCSOrCI(ProgrammingExercise programmingExe * * @param exerciseId of the exercise */ - public void deleteTasksWithSolutionEntries(Long exerciseId) { - List tasks = programmingExerciseTaskRepository.findByExerciseIdWithTestCaseAndSolutionEntriesElseThrow(exerciseId); - Set solutionEntries = tasks.stream().map(ProgrammingExerciseTask::getTestCases).flatMap(Collection::stream) - .map(ProgrammingExerciseTestCase::getSolutionEntries).flatMap(Collection::stream).collect(Collectors.toSet()); + public void deleteTasks(Long exerciseId) { + List tasks = programmingExerciseTaskRepository.findByExerciseIdWithTestCaseElseThrow(exerciseId); programmingExerciseTaskRepository.deleteAll(tasks); - programmingExerciseSolutionEntryRepository.deleteAll(solutionEntries); } private void resetAllStudentBuildPlanIdsForExercise(ProgrammingExercise programmingExercise) { diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/ProgrammingExerciseTaskService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseTaskService.java similarity index 91% rename from src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/ProgrammingExerciseTaskService.java rename to src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseTaskService.java index 0b1a14be8646..3ec052802f83 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/ProgrammingExerciseTaskService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseTaskService.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.service.hestia; +package de.tum.cit.aet.artemis.programming.service; import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; @@ -20,11 +20,10 @@ import org.springframework.stereotype.Service; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTaskRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; @Profile(PROFILE_CORE) @Service @@ -34,8 +33,6 @@ public class ProgrammingExerciseTaskService { private final ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository; - private final ExerciseHintRepository exerciseHintRepository; - /** * Pattern that is used to extract the tasks (capturing group {@code name}) and test case names (capturing groups {@code tests}) from the problem statement. * Example: "[task][Implement BubbleSort](testBubbleSort,testBubbleSortHidden)". Following groups are extracted by the capturing groups: @@ -85,21 +82,18 @@ public class ProgrammingExerciseTaskService { private static final Pattern TESTID_PATTERN = Pattern.compile(TESTID_START + "(\\d+)" + TESTID_END); public ProgrammingExerciseTaskService(ProgrammingExerciseTaskRepository programmingExerciseTaskRepository, - ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository, ExerciseHintRepository exerciseHintRepository) { + ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository) { this.programmingExerciseTaskRepository = programmingExerciseTaskRepository; this.programmingExerciseTestCaseRepository = programmingExerciseTestCaseRepository; - this.exerciseHintRepository = exerciseHintRepository; } /** - * Deletes a ProgrammingExerciseTask together with its CodeHints + * Deletes a ProgrammingExerciseTask * This has to be manually done, as there is no orphanRemoval between the two entities * * @param task The task to delete */ public void delete(ProgrammingExerciseTask task) { - var exerciseHints = exerciseHintRepository.findByTaskId(task.getId()); - exerciseHintRepository.deleteAll(exerciseHints); programmingExerciseTaskRepository.delete(task); } @@ -109,14 +103,13 @@ public void delete(ProgrammingExerciseTask task) { * If there is already a task with the same test cases as a new one, but with a different name the existing one will be renamed. * * @param exercise The programming exercise to extract the tasks from - * @return The current tasks of the exercise */ - public Set updateTasksFromProblemStatement(ProgrammingExercise exercise) { + public void updateTasksFromProblemStatement(ProgrammingExercise exercise) { var previousTasks = programmingExerciseTaskRepository.findByExerciseIdWithTestCases(exercise.getId()); var extractedTasks = new HashSet<>(extractTasks(exercise)); // No changes if (previousTasks.equals(extractedTasks)) { - return previousTasks; + return; } // Add all tasks that did not change var tasksToBeSaved = new HashSet<>(previousTasks); @@ -151,24 +144,7 @@ public Set updateTasksFromProblemStatement(ProgrammingE for (ProgrammingExerciseTask task : tasksToBeSaved) { task.setExercise(exercise); } - return new HashSet<>(programmingExerciseTaskRepository.saveAll(tasksToBeSaved)); - } - - /** - * Gets the tasks of a programming exercise sorted by their order in the problem statement - * TODO: Replace this with an @OrderColumn on tasks in ProgrammingExercise - * - * @param exercise The programming exercise - * @return The sorted tasks - */ - public List getSortedTasks(ProgrammingExercise exercise) { - var unsortedTasks = programmingExerciseTaskRepository.findByExerciseIdWithTestCases(exercise.getId()); - var sortedExtractedTasks = extractTasks(exercise); - return sortedExtractedTasks.stream() - .map(extractedTask -> unsortedTasks.stream() - .filter(task -> task.getTaskName().equals(extractedTask.getTaskName()) && task.getTestCases().equals(extractedTask.getTestCases())).findFirst() - .orElse(null)) - .distinct().filter(Objects::nonNull).toList(); + programmingExerciseTaskRepository.saveAll(tasksToBeSaved); } /** @@ -178,7 +154,7 @@ public List getSortedTasks(ProgrammingExercise exercise * @return Set of all tasks and its test cases */ public Set getTasksWithoutInactiveTestCases(long exerciseId) { - return programmingExerciseTaskRepository.findByExerciseIdWithTestCaseAndSolutionEntriesElseThrow(exerciseId).stream() + return programmingExerciseTaskRepository.findByExerciseIdWithTestCaseElseThrow(exerciseId).stream() .peek(task -> task.getTestCases().removeIf(Predicate.not(ProgrammingExerciseTestCase::isActive))).collect(Collectors.toSet()); } @@ -187,10 +163,10 @@ public Set getTasksWithoutInactiveTestCases(long exerci * Additionally, adds a new task for all test cases with no manually assigned task and adds all tests to that task * * @param exerciseId of the programming exercise - * @return Set of all tasks including one for not manually assigned tests + * @return List of all tasks including one for not manually assigned tests */ public List getTasksWithUnassignedTestCases(long exerciseId) { - List tasks = programmingExerciseTaskRepository.findByExerciseIdWithTestCaseAndSolutionEntriesElseThrow(exerciseId); + List tasks = programmingExerciseTaskRepository.findByExerciseIdWithTestCaseElseThrow(exerciseId); Set testsWithTasks = tasks.stream().flatMap(task -> task.getTestCases().stream()).collect(Collectors.toSet()); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseTestCaseService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseTestCaseService.java index f3770b417e5b..f06a901b8479 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseTestCaseService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseTestCaseService.java @@ -27,7 +27,6 @@ import de.tum.cit.aet.artemis.programming.dto.ProgrammingExerciseTestCaseDTO; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; @Profile(PROFILE_CORE) @Service diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingLanguageFeature.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingLanguageFeature.java index 88bee465e155..df07a8f6d5b3 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingLanguageFeature.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingLanguageFeature.java @@ -13,6 +13,5 @@ */ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record ProgrammingLanguageFeature(ProgrammingLanguage programmingLanguage, boolean sequentialTestRuns, boolean staticCodeAnalysis, boolean plagiarismCheckSupported, - boolean packageNameRequired, boolean checkoutSolutionRepositoryAllowed, List projectTypes, boolean testwiseCoverageAnalysisSupported, - boolean auxiliaryRepositoriesSupported) { + boolean packageNameRequired, boolean checkoutSolutionRepositoryAllowed, List projectTypes, boolean auxiliaryRepositoriesSupported) { } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingSubmissionService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingSubmissionService.java index 9de4485f16b2..e1ae83935eec 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingSubmissionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingSubmissionService.java @@ -67,7 +67,6 @@ import de.tum.cit.aet.artemis.programming.repository.ProgrammingSubmissionRepository; import de.tum.cit.aet.artemis.programming.repository.SubmissionPolicyRepository; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationTriggerService; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseGitDiffReportService; import de.tum.cit.aet.artemis.programming.service.vcs.VersionControlService; // TODO: this class has too many dependencies to other services. We should reduce this diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/Action.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/Action.java deleted file mode 100644 index be22f81ddad9..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/Action.java +++ /dev/null @@ -1,86 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.aeolus; - -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - -/** - * Base class for the actions that can be defined in a {@link Windfile} - */ -// TODO: remove and convert subclasses into Records -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public abstract class Action { - - private String name; - - private Map parameters; - - private Map environment; - - private List results; - - private String workdir; - - private boolean runAlways; - - private String platform; - - public Map getParameters() { - return parameters; - } - - public void setParameters(Map parameters) { - this.parameters = parameters; - } - - public Map getEnvironment() { - return environment; - } - - public void setEnvironment(Map environment) { - this.environment = environment; - } - - public boolean isRunAlways() { - return runAlways; - } - - public void setRunAlways(boolean runAlways) { - this.runAlways = runAlways; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public List getResults() { - return results; - } - - public void setResults(List results) { - this.results = results; - } - - public String getWorkdir() { - return workdir; - } - - public void setWorkdir(String workdir) { - this.workdir = workdir; - } - - public String getPlatform() { - return platform; - } - - public void setPlatform(String platform) { - this.platform = platform; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/ActionDeserializer.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/ActionDeserializer.java index 65d42487e398..32463ba0d841 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/ActionDeserializer.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/ActionDeserializer.java @@ -8,6 +8,10 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Action; +import de.tum.cit.aet.artemis.programming.dto.aeolus.PlatformAction; +import de.tum.cit.aet.artemis.programming.dto.aeolus.ScriptAction; + /** * Deserializer for {@link Action} that determines the type of the action based on the content of the JSON. */ diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildPlanService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildPlanService.java index 9cb410de37fa..9dd35b363709 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildPlanService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildPlanService.java @@ -35,6 +35,8 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; import de.tum.cit.aet.artemis.programming.dto.AeolusGenerationResponseDTO; +import de.tum.cit.aet.artemis.programming.dto.aeolus.AeolusRepository; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; import de.tum.cit.aet.artemis.programming.service.InternalUrlService; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService; diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildScriptGenerationService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildScriptGenerationService.java index 8830adb96389..57141ab1a84a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildScriptGenerationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildScriptGenerationService.java @@ -10,6 +10,8 @@ import de.tum.cit.aet.artemis.core.service.ProfileService; import de.tum.cit.aet.artemis.programming.domain.AeolusTarget; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; +import de.tum.cit.aet.artemis.programming.dto.aeolus.WindfileMetadata; import de.tum.cit.aet.artemis.programming.service.BuildScriptGenerationService; import de.tum.cit.aet.artemis.programming.service.BuildScriptProviderService; @@ -52,12 +54,12 @@ public String getScript(ProgrammingExercise programmingExercise) throws JsonProc windfile = aeolusTemplateService.getDefaultWindfileFor(programmingExercise); } if (windfile != null) { - WindfileMetadata oldMetadata = windfile.getMetadata(); + WindfileMetadata oldMetadata = windfile.metadata(); // Creating a new instance of WindfileMetadata with placeholder values for id, name, and description, // and copying the rest of the fields from oldMetadata WindfileMetadata updatedMetadata = new WindfileMetadata("not-used", "not-used", "not-used", oldMetadata.author(), oldMetadata.gitCredentials(), oldMetadata.docker(), oldMetadata.resultHook(), oldMetadata.resultHookCredentials()); - windfile.setMetadata(updatedMetadata); + windfile = new Windfile(windfile, updatedMetadata); return aeolusBuildPlanService.generateBuildScript(windfile, AeolusTarget.CLI); } return null; diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusTemplateService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusTemplateService.java index 64321ad3b61d..f2e51ab78496 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusTemplateService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusTemplateService.java @@ -26,6 +26,10 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseBuildConfig; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProjectType; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Action; +import de.tum.cit.aet.artemis.programming.dto.aeolus.DockerConfig; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; +import de.tum.cit.aet.artemis.programming.dto.aeolus.WindfileMetadata; import de.tum.cit.aet.artemis.programming.service.BuildScriptProviderService; import de.tum.cit.aet.artemis.programming.web.localci.AeolusTemplateResource; @@ -84,7 +88,7 @@ public void cacheOnBoot() { script = buildScriptProviderService.replacePlaceholders(script, null, null, null); } Windfile windfile = readWindfile(script); - this.addInstanceVariablesToWindfile(windfile, ProgrammingLanguage.valueOf(directory.toUpperCase()), optionalProjectType); + windfile = addInstanceVariablesToWindfile(windfile, ProgrammingLanguage.valueOf(directory.toUpperCase()), optionalProjectType); templateCache.put(uniqueKey, windfile); } catch (IOException | IllegalArgumentException e) { @@ -127,17 +131,15 @@ private static Windfile readWindfile(String yaml) throws IOException { * @param projectType the project type for which the template file should be returned. If omitted, a default depending on the language will be used. * @param staticAnalysis whether the static analysis template should be used * @param sequentialRuns whether the sequential runs template should be used - * @param testCoverage whether the test coverage template should be used * @return the requested template as a {@link Windfile} object * @throws IOException if the file does not exist */ - public Windfile getWindfileFor(ProgrammingLanguage programmingLanguage, Optional projectType, Boolean staticAnalysis, Boolean sequentialRuns, Boolean testCoverage) - throws IOException { + public Windfile getWindfileFor(ProgrammingLanguage programmingLanguage, Optional projectType, Boolean staticAnalysis, Boolean sequentialRuns) throws IOException { if (programmingLanguage.equals(ProgrammingLanguage.JAVA) && projectType.isEmpty()) { // to be backwards compatible, we assume that java exercises without project type are plain maven projects projectType = Optional.of(ProjectType.PLAIN_MAVEN); } - String templateFileName = buildScriptProviderService.buildTemplateName(projectType, staticAnalysis, sequentialRuns, testCoverage, "yaml"); + String templateFileName = buildScriptProviderService.buildTemplateName(projectType, staticAnalysis, sequentialRuns, "yaml"); String uniqueKey = programmingLanguage.name().toLowerCase() + "_" + templateFileName; if (templateCache.containsKey(uniqueKey)) { return templateCache.get(uniqueKey); @@ -151,7 +153,7 @@ public Windfile getWindfileFor(ProgrammingLanguage programmingLanguage, Optional scriptCache = buildScriptProviderService.replacePlaceholders(scriptCache, null, null, null); } Windfile windfile = readWindfile(scriptCache); - this.addInstanceVariablesToWindfile(windfile, programmingLanguage, projectType); + windfile = addInstanceVariablesToWindfile(windfile, programmingLanguage, projectType); templateCache.put(uniqueKey, windfile); return windfile; } @@ -184,7 +186,7 @@ public Windfile getDefaultWindfileFor(ProgrammingExercise exercise) { try { ProgrammingExerciseBuildConfig buildConfig = exercise.getBuildConfig(); return getWindfileFor(exercise.getProgrammingLanguage(), Optional.ofNullable(exercise.getProjectType()), exercise.isStaticCodeAnalysisEnabled(), - buildConfig.hasSequentialTestRuns(), buildConfig.isTestwiseCoverageEnabled()); + buildConfig.hasSequentialTestRuns()); } catch (IOException e) { log.info("No windfile for the settings of exercise {}", exercise.getId(), e); @@ -202,23 +204,24 @@ public Windfile getDefaultWindfileFor(ProgrammingExercise exercise) { * @param windfile the Windfile template to be updated with Docker configuration * @param language the programming language used, which determines the Docker image and flags * @param projectType an optional specifying the project type; influences the Docker configuration + * @return the updated Windfile instance with Docker configuration */ - private void addInstanceVariablesToWindfile(Windfile windfile, ProgrammingLanguage language, Optional projectType) { + private Windfile addInstanceVariablesToWindfile(Windfile windfile, ProgrammingLanguage language, Optional projectType) { - WindfileMetadata metadata = windfile.getMetadata(); + WindfileMetadata metadata = windfile.metadata(); if (metadata == null) { metadata = new WindfileMetadata(); } if (projectType.isPresent() && ProjectType.XCODE.equals(projectType.get())) { // xcode does not support docker metadata = new WindfileMetadata(); - windfile.setMetadata(metadata); - return; } - String image = programmingLanguageConfiguration.getImage(language, projectType); - DockerConfig dockerConfig = new DockerConfig(image, null, null, programmingLanguageConfiguration.getDefaultDockerFlags()); - metadata = new WindfileMetadata(metadata.name(), metadata.id(), metadata.description(), metadata.author(), metadata.gitCredentials(), dockerConfig, metadata.resultHook(), - metadata.resultHookCredentials()); - windfile.setMetadata(metadata); + else { + String image = programmingLanguageConfiguration.getImage(language, projectType); + DockerConfig dockerConfig = new DockerConfig(image, null, null, programmingLanguageConfiguration.getDefaultDockerFlags()); + metadata = new WindfileMetadata(metadata.name(), metadata.id(), metadata.description(), metadata.author(), metadata.gitCredentials(), dockerConfig, + metadata.resultHook(), metadata.resultHookCredentials()); + } + return new Windfile(windfile, metadata); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/PlatformAction.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/PlatformAction.java deleted file mode 100644 index 5c161ffe798b..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/PlatformAction.java +++ /dev/null @@ -1,33 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.aeolus; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - -/** - * Represents a CI action that is intended to run only on a specific target, can be used in a {@link Windfile}. - */ -// TODO: convert into Record -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class PlatformAction extends Action { - - private String kind; - - private String type; - - public String getKind() { - return kind; - } - - public void setKind(String kind) { - this.kind = kind; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/Windfile.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/Windfile.java deleted file mode 100644 index a9f2382591d4..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/Windfile.java +++ /dev/null @@ -1,123 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.aeolus; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; - -/** - * Represents a windfile, the definition file for an aeolus build plan that - * can then be used to generate a Jenkinsfile. - */ -// TODO convert into Record -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class Windfile { - - private static final ObjectMapper mapper = new ObjectMapper(); - - private String api; - - private WindfileMetadata metadata; - - private List actions = new ArrayList<>(); - - private Map repositories = new HashMap<>(); - - public String getApi() { - return api; - } - - public void setApi(String api) { - this.api = api; - } - - public WindfileMetadata getMetadata() { - return metadata; - } - - public void setMetadata(WindfileMetadata metadata) { - this.metadata = metadata; - } - - public List getActions() { - return actions; - } - - public void setActions(List actions) { - this.actions = actions; - } - - /** - * Gets the script actions of a windfile. - * - * @return the script actions of a windfile. - */ - public List getScriptActions() { - List scriptActions = new ArrayList<>(); - for (Action action : actions) { - if (action instanceof ScriptAction) { - scriptActions.add((ScriptAction) action); - } - } - return scriptActions; - } - - public void setRepositories(Map repositories) { - this.repositories = repositories; - } - - public Map getRepositories() { - return repositories; - } - - /** - * Sets the pre-processing metadata for the windfile. - * - * @param id the id of the windfile. - * @param name the name of the windfile. - * @param gitCredentials the git credentials of the windfile. - * @param resultHook the result hook of the windfile. - * @param description the description of the windfile. - * @param repositories the repositories of the windfile. - * @param resultHookCredentials the credentials for the result hook of the windfile. - */ - public void setPreProcessingMetadata(String id, String name, String gitCredentials, String resultHook, String description, Map repositories, - String resultHookCredentials) { - this.setMetadata(new WindfileMetadata(name, id, description, null, gitCredentials, null, resultHook, resultHookCredentials)); - this.setRepositories(repositories); - } - - /** - * Deserializes a windfile from a json string. - * - * @param json the json string to deserialize. - * @return the deserialized windfile. - * @throws JsonProcessingException if the json string is not valid. - */ - public static Windfile deserialize(String json) throws JsonProcessingException { - SimpleModule module = new SimpleModule(); - module.addDeserializer(Action.class, new ActionDeserializer()); - mapper.registerModule(module); - return mapper.readValue(json, Windfile.class); - } - - /** - * Collects the results of all actions of a windfile. - * - * @return the results of all actions of this windfile - */ - public List getResults() { - List results = new ArrayList<>(); - for (Action action : actions.stream().filter(action -> action.getResults() != null && !action.getResults().isEmpty()).toList()) { - results.addAll(action.getResults()); - } - return results; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/AbstractContinuousIntegrationResultService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/AbstractContinuousIntegrationResultService.java index 238d4ffca0fc..2f540cd869b7 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/AbstractContinuousIntegrationResultService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/AbstractContinuousIntegrationResultService.java @@ -11,13 +11,12 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; -import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO; -import de.tum.cit.aet.artemis.programming.dto.BuildJobDTOInterface; +import de.tum.cit.aet.artemis.programming.dto.BuildJobInterface; +import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification; import de.tum.cit.aet.artemis.programming.repository.BuildLogStatisticsEntryRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseFeedbackCreationService; -import de.tum.cit.aet.artemis.programming.service.hestia.TestwiseCoverageService; public abstract class AbstractContinuousIntegrationResultService implements ContinuousIntegrationResultService { @@ -25,32 +24,29 @@ public abstract class AbstractContinuousIntegrationResultService implements Cont protected final BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository; - protected final TestwiseCoverageService testwiseCoverageService; - protected final ProgrammingExerciseFeedbackCreationService feedbackCreationService; protected final ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; protected AbstractContinuousIntegrationResultService(ProgrammingExerciseTestCaseRepository testCaseRepository, - BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, TestwiseCoverageService testwiseCoverageService, - ProgrammingExerciseFeedbackCreationService feedbackCreationService, ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository) { + BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, ProgrammingExerciseFeedbackCreationService feedbackCreationService, + ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository) { this.testCaseRepository = testCaseRepository; this.buildLogStatisticsEntryRepository = buildLogStatisticsEntryRepository; - this.testwiseCoverageService = testwiseCoverageService; this.feedbackCreationService = feedbackCreationService; this.programmingExerciseBuildConfigRepository = programmingExerciseBuildConfigRepository; } @Override - public Result createResultFromBuildResult(AbstractBuildResultNotificationDTO buildResult, ProgrammingExerciseParticipation participation) { + public Result createResultFromBuildResult(BuildResultNotification buildResult, ProgrammingExerciseParticipation participation) { ProgrammingExercise exercise = participation.getProgrammingExercise(); final var result = new Result(); result.setAssessmentType(AssessmentType.AUTOMATIC); result.setSuccessful(buildResult.isBuildSuccessful()); - result.setCompletionDate(buildResult.getBuildRunDate()); + result.setCompletionDate(buildResult.buildRunDate()); // this only sets the score to a temporary value, the real score is calculated in the grading service - result.setScore(buildResult.getBuildScore(), exercise.getCourseViaExerciseGroupOrCourseMember()); + result.setScore(buildResult.buildScore(), exercise.getCourseViaExerciseGroupOrCourseMember()); result.setParticipation((Participation) participation); addFeedbackToResult(result, buildResult); @@ -63,8 +59,8 @@ public Result createResultFromBuildResult(AbstractBuildResultNotificationDTO bui * @param result the result for which the feedback should be added * @param buildResult The build result */ - private void addFeedbackToResult(Result result, AbstractBuildResultNotificationDTO buildResult) { - final var jobs = buildResult.getBuildJobs(); + private void addFeedbackToResult(Result result, BuildResultNotification buildResult) { + final var jobs = buildResult.jobs(); final var programmingExercise = (ProgrammingExercise) result.getParticipation().getExercise(); // 1) add feedback for failed and passed test cases @@ -72,32 +68,28 @@ private void addFeedbackToResult(Result result, AbstractBuildResultNotificationD // 2) process static code analysis feedback addStaticCodeAnalysisFeedbackToResult(result, buildResult, programmingExercise); - - // 3) process testwise coverage analysis report - addTestwiseCoverageReportToResult(result, buildResult, programmingExercise); } - private void addTestCaseFeedbacksToResult(Result result, List jobs, ProgrammingExercise programmingExercise) { + private void addTestCaseFeedbacksToResult(Result result, List jobs, ProgrammingExercise programmingExercise) { var activeTestCases = testCaseRepository.findByExerciseIdAndActive(programmingExercise.getId(), true); - for (final var job : jobs) { - for (final var failedTest : job.getFailedTests()) { - result.addFeedback( - feedbackCreationService.createFeedbackFromTestCase(failedTest.getName(), failedTest.getTestMessages(), false, programmingExercise, activeTestCases)); - } - result.setTestCaseCount(result.getTestCaseCount() + job.getFailedTests().size()); + jobs.forEach(job -> { + job.failedTests().forEach(failedTest -> { + result.addFeedback(feedbackCreationService.createFeedbackFromTestCase(failedTest.name(), failedTest.testMessages(), false, programmingExercise, activeTestCases)); + }); + result.setTestCaseCount(result.getTestCaseCount() + job.failedTests().size()); - for (final var successfulTest : job.getSuccessfulTests()) { + for (final var successfulTest : job.successfulTests()) { result.addFeedback( - feedbackCreationService.createFeedbackFromTestCase(successfulTest.getName(), successfulTest.getTestMessages(), true, programmingExercise, activeTestCases)); + feedbackCreationService.createFeedbackFromTestCase(successfulTest.name(), successfulTest.testMessages(), true, programmingExercise, activeTestCases)); } - result.setTestCaseCount(result.getTestCaseCount() + job.getSuccessfulTests().size()); - result.setPassedTestCaseCount(result.getPassedTestCaseCount() + job.getSuccessfulTests().size()); - } + result.setTestCaseCount(result.getTestCaseCount() + job.successfulTests().size()); + result.setPassedTestCaseCount(result.getPassedTestCaseCount() + job.successfulTests().size()); + }); } - private void addStaticCodeAnalysisFeedbackToResult(Result result, AbstractBuildResultNotificationDTO buildResult, ProgrammingExercise programmingExercise) { - final var staticCodeAnalysisReports = buildResult.getStaticCodeAnalysisReports(); + private void addStaticCodeAnalysisFeedbackToResult(Result result, BuildResultNotification buildResult, ProgrammingExercise programmingExercise) { + final var staticCodeAnalysisReports = buildResult.staticCodeAnalysisReports(); if (Boolean.TRUE.equals(programmingExercise.isStaticCodeAnalysisEnabled()) && staticCodeAnalysisReports != null && !staticCodeAnalysisReports.isEmpty()) { List scaFeedbackList = feedbackCreationService.createFeedbackFromStaticCodeAnalysisReports(staticCodeAnalysisReports); result.addFeedbacks(scaFeedbackList); @@ -105,18 +97,6 @@ private void addStaticCodeAnalysisFeedbackToResult(Result result, AbstractBuildR } } - private void addTestwiseCoverageReportToResult(Result result, AbstractBuildResultNotificationDTO buildResult, ProgrammingExercise programmingExercise) { - programmingExercise.setBuildConfig(programmingExerciseBuildConfigRepository.getProgrammingExerciseBuildConfigElseThrow(programmingExercise)); - if (Boolean.TRUE.equals(programmingExercise.getBuildConfig().isTestwiseCoverageEnabled())) { - var report = buildResult.getTestwiseCoverageReports(); - if (report != null) { - // since the test cases are not saved to the database yet, the test case is null for the entries - var coverageFileReportsWithoutTestsByTestCaseName = testwiseCoverageService.createTestwiseCoverageFileReportsWithoutTestsByTestCaseName(report); - result.setCoverageFileReportsByTestCaseName(coverageFileReportsWithoutTestsByTestCaseName); - } - } - } - /** * Find the ZonedDateTime of the first BuildLogEntry that contains the searchString in the log message. * diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationResultService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationResultService.java index b81239c44f0e..a63fc4871d4c 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationResultService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationResultService.java @@ -8,7 +8,7 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; -import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO; +import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification; /** * Abstract service for managing entities related to continuous integration. @@ -21,7 +21,7 @@ public interface ContinuousIntegrationResultService { * @param requestBody the object sent from the CI system to Artemis * @return the DTO with all information in Java Object form */ - AbstractBuildResultNotificationDTO convertBuildResult(Object requestBody); + BuildResultNotification convertBuildResult(Object requestBody); /** * Generate an Artemis result object from the CI build result. Will use the test case results and issues in static code analysis as result feedback. @@ -30,7 +30,7 @@ public interface ContinuousIntegrationResultService { * @param participation to attach result to. * @return the created Artemis result with a score, completion date, etc. */ - Result createResultFromBuildResult(AbstractBuildResultNotificationDTO buildResult, ProgrammingExerciseParticipation participation); + Result createResultFromBuildResult(BuildResultNotification buildResult, ProgrammingExerciseParticipation participation); /** * Extract the build log statistics from the BuildLogEntries and persist a BuildLogStatisticsEntry. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestCaseDTO.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestCaseDTO.java index e4e15c9227bf..5ae570f5b2d5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestCaseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestCaseDTO.java @@ -14,13 +14,13 @@ import com.fasterxml.jackson.annotation.Nulls; import de.tum.cit.aet.artemis.assessment.domain.Feedback; -import de.tum.cit.aet.artemis.programming.dto.TestCaseBaseDTO; +import de.tum.cit.aet.artemis.programming.dto.TestCaseBase; @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record TestCaseDTO(String name, String classname, double time, @JsonProperty("failures") @JsonSetter(nulls = Nulls.AS_EMPTY) List failures, @JsonProperty("errors") @JsonSetter(nulls = Nulls.AS_EMPTY) List errors, - @JsonProperty("successInfos") @JsonSetter(nulls = Nulls.AS_EMPTY) List successInfos) implements TestCaseBaseDTO { + @JsonProperty("successInfos") @JsonSetter(nulls = Nulls.AS_EMPTY) List successInfos) implements TestCaseBase { @JsonIgnore public boolean isSuccessful() { @@ -28,12 +28,7 @@ public boolean isSuccessful() { } @Override - public String getName() { - return name; - } - - @Override - public List getTestMessages() { + public List testMessages() { return extractMessage().map(Collections::singletonList).orElse(Collections.emptyList()); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestResultsDTO.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestResultsDTO.java index cd029f5adf5e..9bb2e13c588a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestResultsDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestResultsDTO.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.stream.Stream; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -15,156 +14,64 @@ import de.tum.cit.aet.artemis.programming.domain.RepositoryType; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; -import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO; -import de.tum.cit.aet.artemis.programming.dto.BuildJobDTOInterface; +import de.tum.cit.aet.artemis.programming.dto.BuildJobInterface; +import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification; import de.tum.cit.aet.artemis.programming.dto.StaticCodeAnalysisReportDTO; import de.tum.cit.aet.artemis.programming.service.ci.notification.BuildLogParseUtils; @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) // Note: due to limitations with inheritance, we cannot declare this as record, but we can use it in a similar way with final fields -public class TestResultsDTO extends AbstractBuildResultNotificationDTO { - - private final int successful; - - private final int skipped; - - private final int errors; - - private final int failures; - - private final String fullName; - - private final List commits; - - private final List results; - - private final List staticCodeAnalysisReports; - - private final List testwiseCoverageReport; - - private final ZonedDateTime runDate; - - private final boolean isBuildSuccessful; - - private final List logs; - - @JsonCreator - public TestResultsDTO(@JsonProperty("successful") int successful, @JsonProperty("skipped") int skipped, @JsonProperty("errors") int errors, - @JsonProperty("failures") int failures, @JsonProperty("fullName") String fullName, @JsonProperty("commits") @JsonSetter(nulls = Nulls.AS_EMPTY) List commits, - @JsonProperty("results") @JsonSetter(nulls = Nulls.AS_EMPTY) List results, - @JsonProperty("staticCodeAnalysisReports") @JsonSetter(nulls = Nulls.AS_EMPTY) List staticCodeAnalysisReports, - @JsonProperty("testwiseCoverageReport") @JsonSetter(nulls = Nulls.AS_EMPTY) List testwiseCoverageReport, - @JsonProperty("runDate") ZonedDateTime runDate, @JsonProperty("isBuildSuccessful") boolean isBuildSuccessful, - @JsonProperty("logs") @JsonSetter(nulls = Nulls.AS_EMPTY) List logs) { - this.successful = successful; - this.skipped = skipped; - this.errors = errors; - this.failures = failures; - this.fullName = fullName; - this.commits = commits; - this.results = results; - this.staticCodeAnalysisReports = staticCodeAnalysisReports; - this.testwiseCoverageReport = testwiseCoverageReport; - this.runDate = runDate; - this.isBuildSuccessful = isBuildSuccessful; - this.logs = logs; - } +public record TestResultsDTO(@JsonProperty("successful") int successful, @JsonProperty("skipped") int skipped, @JsonProperty("errors") int errors, + @JsonProperty("failures") int failures, @JsonProperty("fullName") String fullName, @JsonProperty("commits") @JsonSetter(nulls = Nulls.AS_EMPTY) List commits, + @JsonProperty("results") @JsonSetter(nulls = Nulls.AS_EMPTY) List results, + @JsonProperty("staticCodeAnalysisReports") @JsonSetter(nulls = Nulls.AS_EMPTY) List staticCodeAnalysisReports, + @JsonProperty("runDate") ZonedDateTime runDate, @JsonProperty("isBuildSuccessful") boolean isBuildSuccessful, + @JsonProperty("logs") @JsonSetter(nulls = Nulls.AS_EMPTY) List logs) implements BuildResultNotification { public static TestResultsDTO convert(Object someResult) { return new ObjectMapper().registerModule(new JavaTimeModule()).convertValue(someResult, TestResultsDTO.class); } - public int getSuccessful() { - return successful; - } - - public int getSkipped() { - return skipped; - } - - public int getErrors() { - return errors; - } - - public int getFailures() { - return failures; - } - - public String getFullName() { - return fullName; - } - - public ZonedDateTime getRunDate() { - return runDate; - } - - public List getLogs() { - return this.logs; - } - @Override - public ZonedDateTime getBuildRunDate() { - return getRunDate(); + public ZonedDateTime buildRunDate() { + return runDate(); } @Override - protected String getCommitHashFromAssignmentRepo() { + public String assignmentRepoCommitHash() { final var testRepoNameSuffix = RepositoryType.TESTS.getName(); - final var firstCommit = getCommits().stream().filter(commit -> !commit.repositorySlug().endsWith(testRepoNameSuffix)).findFirst(); + final var firstCommit = commits().stream().filter(commit -> !commit.repositorySlug().endsWith(testRepoNameSuffix)).findFirst(); return firstCommit.map(CommitDTO::hash).orElse(null); } @Override - protected String getCommitHashFromTestsRepo() { + public String testsRepoCommitHash() { final var testRepoNameSuffix = RepositoryType.TESTS.getName(); - final var firstCommit = getCommits().stream().filter(commit -> commit.repositorySlug().endsWith(testRepoNameSuffix)).findFirst(); + final var firstCommit = commits().stream().filter(commit -> commit.repositorySlug().endsWith(testRepoNameSuffix)).findFirst(); return firstCommit.map(CommitDTO::hash).orElse(null); } @Override - public String getBranchNameFromAssignmentRepo() { + public String assignmentRepoBranchName() { final var testRepoNameSuffix = RepositoryType.TESTS.getName(); - final var firstCommit = getCommits().stream().filter(commit -> !commit.repositorySlug().endsWith(testRepoNameSuffix)).findFirst(); + final var firstCommit = commits().stream().filter(commit -> !commit.repositorySlug().endsWith(testRepoNameSuffix)).findFirst(); return firstCommit.map(CommitDTO::branchName).orElse(null); } private int getSum() { - return getSkipped() + getFailures() + getErrors() + getSuccessful(); - } - - @Override - public boolean isBuildSuccessful() { - return isBuildSuccessful; + return skipped() + failures() + errors() + successful(); } @Override - public Double getBuildScore() { + public Double buildScore() { final var testSum = getSum(); - return testSum == 0 ? 0D : ((double) getSuccessful() / testSum) * 100D; - } - - public List getCommits() { - return commits; - } - - public List getResults() { - return results; - } - - @Override - public List getStaticCodeAnalysisReports() { - return staticCodeAnalysisReports; - } - - @Override - public List getTestwiseCoverageReports() { - return testwiseCoverageReport; + return testSum == 0 ? 0D : ((double) successful() / testSum) * 100D; } @Override public boolean hasArtifact() { - // TODO: this is not available in Jenkins or GitLab CI yet + // NOTE: this is not available in Jenkins return false; } @@ -175,13 +82,13 @@ public boolean hasLogs() { @Override public List extractBuildLogs() { - var buildLogs = BuildLogParseUtils.parseBuildLogsFromLogs(getLogs()); + var buildLogs = BuildLogParseUtils.parseBuildLogsFromLogs(logs()); return filterBuildLogs(buildLogs); } @Override - public List getBuildJobs() { - return getResults(); + public List jobs() { + return results(); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestSuiteDTO.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestSuiteDTO.java index 9be20c1a8ad9..ac3702222c00 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestSuiteDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestSuiteDTO.java @@ -9,23 +9,23 @@ import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.Nulls; -import de.tum.cit.aet.artemis.programming.dto.BuildJobDTOInterface; -import de.tum.cit.aet.artemis.programming.dto.TestCaseBaseDTO; +import de.tum.cit.aet.artemis.programming.dto.BuildJobInterface; +import de.tum.cit.aet.artemis.programming.dto.TestCaseBase; @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record TestSuiteDTO(String name, double time, int errors, int skipped, int failures, int tests, - @JsonProperty("testCases") @JsonSetter(nulls = Nulls.AS_EMPTY) List testCases) implements BuildJobDTOInterface { + @JsonProperty("testCases") @JsonSetter(nulls = Nulls.AS_EMPTY) List testCases) implements BuildJobInterface { @Override @JsonIgnore - public List getFailedTests() { + public List failedTests() { return testCases.stream().filter(testCase -> !testCase.isSuccessful()).toList(); } @Override @JsonIgnore - public List getSuccessfulTests() { + public List successfulTests() { return testCases.stream().filter(TestCaseDTO::isSuccessful).toList(); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestwiseCoverageReportDTO.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestwiseCoverageReportDTO.java deleted file mode 100644 index ef94ea89542f..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestwiseCoverageReportDTO.java +++ /dev/null @@ -1,115 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.ci.notification.dto; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -// TODO: convert to record -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class TestwiseCoverageReportDTO implements Serializable { - - @JsonProperty("uniformPath") - private String uniformPath; - - @JsonProperty("duration") - private double duration; - - @JsonProperty("content") - private String content; - - @JsonProperty("paths") - private List coveredPathsPerTestDTOs = new ArrayList<>(); - - public String getUniformPath() { - return uniformPath; - } - - public void setUniformPath(String uniformPath) { - this.uniformPath = uniformPath; - } - - public double getDuration() { - return duration; - } - - public void setDuration(double duration) { - this.duration = duration; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public List getCoveredPathsPerTestDTOs() { - return coveredPathsPerTestDTOs; - } - - public void setCoveredPathsPerTestDTOs(List coveredPathsPerTestDTOs) { - this.coveredPathsPerTestDTOs = coveredPathsPerTestDTOs; - } - - // TODO: convert to record - @JsonIgnoreProperties(ignoreUnknown = true) - @JsonInclude(JsonInclude.Include.NON_EMPTY) - public static final class CoveredPathsPerTestDTO implements Serializable { - - @JsonProperty("path") - private String path; - - @JsonProperty("files") - private List coveredFilesPerTestDTOs = new ArrayList<>(); - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - public List getCoveredFilesPerTestDTOs() { - return coveredFilesPerTestDTOs; - } - - public void setCoveredFilesPerTestDTOs(List coveredFilesPerTestDTOs) { - this.coveredFilesPerTestDTOs = coveredFilesPerTestDTOs; - } - } - - // TODO: convert to record - @JsonIgnoreProperties(ignoreUnknown = true) - @JsonInclude(JsonInclude.Include.NON_EMPTY) - public static final class CoveredFilesPerTestDTO implements Serializable { - - @JsonProperty("fileName") - private String fileName; - - @JsonProperty("coveredLines") - private String coveredLinesWithRanges; - - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } - - public String getCoveredLinesWithRanges() { - return coveredLinesWithRanges; - } - - public void setCoveredLinesWithRanges(String coveredLinesWithRanges) { - this.coveredLinesWithRanges = coveredLinesWithRanges; - } - } -} 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 0c71114e13bb..357cf05d97f8 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 @@ -23,9 +23,9 @@ public class GitLabCIProgrammingLanguageFeatureService extends ProgrammingLanguageFeatureService { 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(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)); + programmingLanguageFeatures.put(EMPTY, new ProgrammingLanguageFeature(EMPTY, false, false, false, false, false, List.of(), false)); + programmingLanguageFeatures.put(JAVA, new ProgrammingLanguageFeature(JAVA, false, false, false, true, false, List.of(PLAIN_MAVEN, MAVEN_MAVEN), false)); + programmingLanguageFeatures.put(JAVASCRIPT, new ProgrammingLanguageFeature(JAVASCRIPT, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), false)); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIResultService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIResultService.java index fa63482e88ca..0c6475c1e236 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIResultService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIResultService.java @@ -8,22 +8,18 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import de.tum.cit.aet.artemis.assessment.repository.FeedbackRepository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogStatisticsEntry; -import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO; +import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification; import de.tum.cit.aet.artemis.programming.repository.BuildLogStatisticsEntryRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingSubmissionRepository; -import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseFeedbackCreationService; import de.tum.cit.aet.artemis.programming.service.ci.AbstractContinuousIntegrationResultService; import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.TestResultsDTO; -import de.tum.cit.aet.artemis.programming.service.hestia.TestwiseCoverageService; // Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 @Deprecated(since = "7.5.0", forRemoval = true) @@ -34,15 +30,13 @@ public class GitLabCIResultService extends AbstractContinuousIntegrationResultSe private static final Logger log = LoggerFactory.getLogger(GitLabCIResultService.class); - public GitLabCIResultService(ProgrammingSubmissionRepository programmingSubmissionRepository, FeedbackRepository feedbackRepository, BuildLogEntryService buildLogService, - BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, TestwiseCoverageService testwiseCoverageService, - ProgrammingExerciseFeedbackCreationService feedbackCreationService, ProgrammingExerciseTestCaseRepository testCaseRepository, - ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository) { - super(testCaseRepository, buildLogStatisticsEntryRepository, testwiseCoverageService, feedbackCreationService, programmingExerciseBuildConfigRepository); + public GitLabCIResultService(BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, ProgrammingExerciseFeedbackCreationService feedbackCreationService, + ProgrammingExerciseTestCaseRepository testCaseRepository, ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository) { + super(testCaseRepository, buildLogStatisticsEntryRepository, feedbackCreationService, programmingExerciseBuildConfigRepository); } @Override - public AbstractBuildResultNotificationDTO convertBuildResult(Object requestBody) { + public BuildResultNotification convertBuildResult(Object requestBody) { return TestResultsDTO.convert(requestBody); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIService.java index e0ec98ed52cf..7226e40b16db 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIService.java @@ -314,7 +314,7 @@ public void deleteBuildPlan(String projectKey, String buildPlanId) { @Override public String getPlanKey(Object requestBody) throws ContinuousIntegrationException { TestResultsDTO dto = TestResultsDTO.convert(requestBody); - return dto.getFullName(); + return dto.fullName(); } @Override diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/CodeHintService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/CodeHintService.java deleted file mode 100644 index f04de8a56db9..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/CodeHintService.java +++ /dev/null @@ -1,187 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; - -import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; -import de.tum.cit.aet.artemis.programming.repository.hestia.CodeHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; - -@Profile(PROFILE_CORE) -@Service -public class CodeHintService { - - private static final Logger log = LoggerFactory.getLogger(CodeHintService.class); - - private final CodeHintRepository codeHintRepository; - - private final ProgrammingExerciseTaskRepository taskRepository; - - private final ProgrammingExerciseSolutionEntryRepository solutionEntryRepository; - - public CodeHintService(CodeHintRepository codeHintRepository, ProgrammingExerciseTaskRepository taskRepository, - ProgrammingExerciseSolutionEntryRepository solutionEntryRepository) { - this.codeHintRepository = codeHintRepository; - this.taskRepository = taskRepository; - this.solutionEntryRepository = solutionEntryRepository; - } - - /** - * Generate {@link CodeHint}s for all {@link ProgrammingExerciseTask}s of an exercise. - * If requested old code hints will be deleted otherwise the new ones will be added to the existing ones. (This might however break the old ones) - * If a task does not have any test cases with solution entries it will not get a code hint. - * - * @param exercise The programming exercise - * @param deleteOldCodeHints Whether old code hint should be deleted - * @return The list of all newly generated code hints - */ - public List generateCodeHintsForExercise(ProgrammingExercise exercise, boolean deleteOldCodeHints) { - log.info("Generating code hints for exercise {} with deleteOldCodeHints={}", exercise.getId(), deleteOldCodeHints); - - var tasks = taskRepository.findByExerciseIdWithTestCaseAndSolutionEntriesElseThrow(exercise.getId()); - - return tasks.stream().map(task -> generateCodeHintForTask(task, deleteOldCodeHints)).filter(Optional::isPresent).map(Optional::get).toList(); - } - - /** - * Generate a single {@link CodeHint} for a single {@link ProgrammingExerciseTask} - * If requested old code hints will be deleted otherwise the new one will be added to the existing ones. (This might however break the old ones) - * If the task does not have any test cases with solution entries it will not get a code hint. - * - * @param task The programming exercise task - * @param deleteOldCodeHints Whether old code hint should be deleted - * @return The newly created code hint if one was needed - */ - public Optional generateCodeHintForTask(ProgrammingExerciseTask task, boolean deleteOldCodeHints) { - log.info("Generating code hints for task {} ({}) in exercise {} with deleteOldCodeHints={}", task.getId(), task.getTaskName(), task.getExercise().getId(), - deleteOldCodeHints); - - var codeHint = new CodeHint(); - codeHint.setExercise(task.getExercise()); - codeHint.setProgrammingExerciseTask(task); - codeHint.setTitle("Code hint for task " + task.getTaskName()); - - var solutionEntries = task.getTestCases().stream().flatMap(testCase -> testCase.getSolutionEntries().stream()).peek(solutionEntry -> solutionEntry.setCodeHint(codeHint)) - .collect(Collectors.toSet()); - - if (deleteOldCodeHints) { - deleteCodeHintsForTask(task); - } - if (solutionEntries.isEmpty()) { - return Optional.empty(); - } - - codeHint.setSolutionEntries(solutionEntries); - codeHintRepository.save(codeHint); - solutionEntryRepository.saveAll(solutionEntries); - - return Optional.of(codeHint); - } - - /** - * Deletes all code hints of a {@link ProgrammingExerciseTask} - * - * @param task The programming exercise task - */ - public void deleteCodeHintsForTask(ProgrammingExerciseTask task) { - log.info("Deleting all code hints of task {} ({}) in exercise {}", task.getId(), task.getTaskName(), task.getExercise().getId()); - - var codeHints = codeHintRepository.findByTaskIdWithSolutionEntries(task.getId()); - var solutionEntries = codeHints.stream().flatMap(codeHint -> codeHint.getSolutionEntries().stream()).peek(solutionEntry -> solutionEntry.setCodeHint(null)).toList(); - solutionEntryRepository.saveAll(solutionEntries); - codeHintRepository.deleteAll(codeHints); - } - - /** - * Deletes a single code hint. - * Sets the code hint of all related solution entries to null before deleting. - * - * @param codeHint The code hint to be deleted - */ - public void deleteCodeHint(CodeHint codeHint) { - log.info("Deleting code hint {}", codeHint.getId()); - - var solutionEntries = solutionEntryRepository.findByCodeHintId(codeHint.getId()); - for (ProgrammingExerciseSolutionEntry solutionEntry : solutionEntries) { - solutionEntry.setCodeHint(null); - } - solutionEntryRepository.saveAll(solutionEntries); - codeHintRepository.delete(codeHint); - } - - /** - * Persists the updated solution entries for a code hint. The solution entries are loaded from the database and can result in three scenarios: - * 1. The code or test case for an existing entry is updated. - * 2. A new solution entry is created. The test case of the entry must be contained in hint related task. - * 3. An entry is removed from the code hint. This method does not delete the entry itself, but removes the reference to the corresponding hint. - * - * @param hint the code hint containing the solution entries to be updated - */ - public void updateSolutionEntriesForCodeHint(CodeHint hint) { - var optionalTask = taskRepository.findByCodeHintIdWithTestCases(hint.getId()); - - if (optionalTask.isEmpty()) { - throw new BadRequestAlertException("No task has been assigned to the code hint.", "CodeHint", "codeHint"); - } - - var task = optionalTask.get(); - - var savedSolutionEntries = solutionEntryRepository.findByCodeHintId(hint.getId()); - - var newEntries = hint.getSolutionEntries().stream().filter(entry -> savedSolutionEntries.stream().noneMatch(savedEntry -> savedEntry.getId().equals(entry.getId()))) - .peek(entry -> entry.setCodeHint(hint)).toList(); - - // check that the task of the hint contains all test cases of every solution entry to be saved - // we only assume changes on the test case of an entry, if the value is defined - boolean hasUnrelatedTestCaseEntries = newEntries.stream() - .anyMatch(entry -> task.getTestCases().stream().noneMatch(containedTestCase -> containedTestCase.getId().equals(entry.getTestCase().getId()))); - if (hasUnrelatedTestCaseEntries) { - throw new BadRequestAlertException("There is at least one solution entry that references a test case that does not belong to the task of the code hint.", "Code Hint", - "codeHint"); - } - - var result = new HashSet<>(newEntries); - - var updatedEntries = new HashSet<>(hint.getSolutionEntries()); - newEntries.forEach(updatedEntries::remove); - - var removedEntries = savedSolutionEntries.stream() - .filter(savedEntry -> hint.getSolutionEntries().stream().noneMatch(updatedEntry -> savedEntry.getId().equals(updatedEntry.getId()))) - .peek(entryToRemove -> entryToRemove.setCodeHint(null)).toList(); - - updatedEntries.forEach(updatedEntry -> { - var optionalMatch = savedSolutionEntries.stream().filter(savedEntry -> savedEntry.getId().equals(updatedEntry.getId())).findFirst(); - if (optionalMatch.isPresent()) { - var match = optionalMatch.get(); - match.setLine(updatedEntry.getLine()); - match.setPreviousLine(updatedEntry.getPreviousLine()); - match.setFilePath(updatedEntry.getFilePath()); - match.setCode(updatedEntry.getCode()); - match.setPreviousCode(updatedEntry.getPreviousCode()); - - // update test case if defined - match.setTestCase(updatedEntry.getTestCase() != null ? updatedEntry.getTestCase() : match.getTestCase()); - result.add(match); - } - }); - hint.setSolutionEntries(result); - solutionEntryRepository.saveAll(result); - solutionEntryRepository.saveAll(removedEntries); - - codeHintRepository.save(hint); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/ExerciseHintService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/ExerciseHintService.java deleted file mode 100644 index 477bcaf88749..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/ExerciseHintService.java +++ /dev/null @@ -1,326 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import org.springframework.cache.annotation.Cacheable; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; - -import de.tum.cit.aet.artemis.assessment.domain.Feedback; -import de.tum.cit.aet.artemis.core.domain.User; -import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; -import de.tum.cit.aet.artemis.core.exception.ConflictException; -import de.tum.cit.aet.artemis.core.security.Role; -import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; -import de.tum.cit.aet.artemis.exercise.domain.Submission; -import de.tum.cit.aet.artemis.exercise.repository.StudentParticipationRepository; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHintActivation; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintActivationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintRepository; - -@Profile(PROFILE_CORE) -@Service -public class ExerciseHintService { - - private final AuthorizationCheckService authCheckService; - - private final ExerciseHintRepository exerciseHintRepository; - - private final ProgrammingExerciseTaskService programmingExerciseTaskService; - - private final StudentParticipationRepository studentParticipationRepository; - - private final ExerciseHintActivationRepository exerciseHintActivationRepository; - - public ExerciseHintService(AuthorizationCheckService authCheckService, ExerciseHintRepository exerciseHintRepository, - ProgrammingExerciseTaskService programmingExerciseTaskService, StudentParticipationRepository studentParticipationRepository, - ExerciseHintActivationRepository exerciseHintActivationRepository) { - this.authCheckService = authCheckService; - this.exerciseHintRepository = exerciseHintRepository; - this.programmingExerciseTaskService = programmingExerciseTaskService; - this.studentParticipationRepository = studentParticipationRepository; - this.exerciseHintActivationRepository = exerciseHintActivationRepository; - } - - /** - * Copies the hints of an exercise to a new target exercise by cloning the hint objects and saving them - * resulting in new IDs for the copied hints. The contents stay the same. On top of that, all hints in the - * problem statement of the target exercise get replaced by the new IDs. - * - * @param template The template exercise containing the hints that should be copied - * @param target The new target exercise, to which all hints should get copied to. - * @return A map with the old hint id as a key and the new hint id as a value - */ - public Map copyExerciseHints(final ProgrammingExercise template, final ProgrammingExercise target) { - final Map hintIdMapping = new HashMap<>(); - target.setExerciseHints(template.getExerciseHints().stream().map(hint -> { - ExerciseHint copiedHint = hint.createCopy(); - copiedHint.setExercise(target); - exerciseHintRepository.save(copiedHint); - hintIdMapping.put(hint.getId(), copiedHint.getId()); - return copiedHint; - }).collect(Collectors.toSet())); - - return hintIdMapping; - } - - /** - * Sets the rating of an exercise hint for a user - * The rating is saved in the associated ExerciseHintActivation. - * - * @param exerciseHint The exercise hint to rate - * @param user The user that submits the rating - * @param ratingValue The value of the rating - */ - public void rateExerciseHint(ExerciseHint exerciseHint, User user, Integer ratingValue) { - if (ratingValue < 1 || ratingValue > 5) { - throw new BadRequestAlertException("rating has to be between 1 and 5", "exerciseHint", "ratingValue.invalid", false); - } - var exerciseHintActivation = exerciseHintActivationRepository.findByExerciseHintAndUserElseThrow(exerciseHint.getId(), user.getId()); - exerciseHintActivation.setRating(ratingValue); - exerciseHintActivationRepository.save(exerciseHintActivation); - } - - /** - * Activates an ExerciseHint for a user. - * After activation the user can view the full content of the hint without restrictions. - * This action cannot be undone - * - * @param exerciseHint The exercise hint - * @param user The user - * @return true if the hint was activated - */ - public boolean activateHint(ExerciseHint exerciseHint, User user) { - // Check if the user has access to the exercise - // This is done here to prevent illegal activation of hints in all possible future cases - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.STUDENT, exerciseHint.getExercise(), user); - - // Check if the user can activate the hint - if (!getAvailableExerciseHints(exerciseHint.getExercise(), user).contains(exerciseHint)) { - return false; - } - - // Check if the hint was already activated - if (exerciseHintActivationRepository.findByExerciseHintAndUser(exerciseHint.getId(), user.getId()).isPresent()) { - return false; - } - - var exerciseHintActivation = new ExerciseHintActivation(); - exerciseHintActivation.setExerciseHint(exerciseHint); - exerciseHintActivation.setUser(user); - exerciseHintActivation.setActivationDate(ZonedDateTime.now()); - exerciseHintActivationRepository.save(exerciseHintActivation); - return true; - } - - /** - * Retrieves all exercise hints that a user has activated for an exercise. - * - * @param exercise The programming exercise - * @param user The user - * @return All activated exercise hints for this user and exercise - */ - public Set getActivatedExerciseHints(ProgrammingExercise exercise, User user) { - return exerciseHintActivationRepository.findByExerciseAndUserWithExerciseHintRelations(exercise.getId(), user.getId()).stream().map(exerciseHintActivation -> { - var exerciseHint = exerciseHintActivation.getExerciseHint(); - exerciseHint.setCurrentUserRating(exerciseHintActivation.getRating()); - return exerciseHint; - }).collect(Collectors.toSet()); - } - - /** - * Returns all exercise hints that the user can currently activate for a given programming exercise. - * Exercise hints will be shown for the first task that meets the following conditions: - * (1) the subsequent number of the latest submissions the previous task is successful is greater or equal to the hint's threshold - * (2) the subsequent number of the latest submissions the current task is unsuccessful is greater or equal to the hint's threshold - * If no task matches these conditions, no exercise hints will be returned - * Note: A task is successful, if the feedback within the submission is positive for all associated test cases within this task - * - * @param exercise The programming exercise - * @param user The user - * @return All available exercise hints - */ - public Set getAvailableExerciseHints(ProgrammingExercise exercise, User user) { - var exerciseHints = exerciseHintRepository.findByExerciseId(exercise.getId()); - if (exerciseHints.isEmpty()) { - return new HashSet<>(); - } - - var submissions = getSubmissionsForStudent(exercise, user); - - if (submissions.isEmpty()) { - return new HashSet<>(); - } - - var latestResult = submissions.getFirst().getLatestResult(); - - // latest submissions has no result or latest result has no feedback (most commonly due to a build error) - if (latestResult == null || latestResult.getFeedbacks().isEmpty()) { - return new HashSet<>(); - } - var tasks = programmingExerciseTaskService.getSortedTasks(exercise); - - var subsequentNumberOfUnsuccessfulSubmissionsByTask = tasks.stream() - .collect(Collectors.toMap(task -> task, task -> subsequentNumberOfSubmissionsForTaskWithStatus(submissions, task, false))); - var subsequentNumberOfSuccessfulSubmissionsByTask = tasks.stream() - .collect(Collectors.toMap(task -> task, task -> subsequentNumberOfSubmissionsForTaskWithStatus(submissions, task, true))); - - var availableHints = new HashSet(); - - for (int i = 0; i < tasks.size(); i++) { - var task = tasks.get(i); - int subsequentNumberOfUnsuccessfulSubmissionsForCurrentTask = subsequentNumberOfUnsuccessfulSubmissionsByTask.get(task); - // current task is successful - if (subsequentNumberOfUnsuccessfulSubmissionsForCurrentTask == 0) { - continue; - } - - var hintsInTask = exerciseHints.stream() - .filter(hint -> hint.getProgrammingExerciseTask() != null && Objects.equals(hint.getProgrammingExerciseTask().getId(), task.getId())) - .collect(Collectors.toSet()); - // no hints exist for the current task - if (hintsInTask.isEmpty()) { - continue; - } - - Optional subsequentNumberSuccessfulSubmissionsForPreviousTask; - if (i == 0) { - subsequentNumberSuccessfulSubmissionsForPreviousTask = Optional.empty(); - } - else { - subsequentNumberSuccessfulSubmissionsForPreviousTask = Optional.of(subsequentNumberOfSuccessfulSubmissionsByTask.get(tasks.get(i - 1))); - } - - // skip current task if the previous task was not successful - if (0 == subsequentNumberSuccessfulSubmissionsForPreviousTask.orElse(-1)) { - continue; - } - - // add the available hints for the current task - var availableHintsForCurrentTask = getAvailableExerciseHintsForTask(subsequentNumberSuccessfulSubmissionsForPreviousTask, - subsequentNumberOfUnsuccessfulSubmissionsForCurrentTask, hintsInTask); - if (!availableHintsForCurrentTask.isEmpty()) { - availableHints.addAll(availableHintsForCurrentTask); - break; - } - } - // Hints with a threshold of 0 will always be displayed - availableHints.addAll(exerciseHints.stream().filter(hint -> hint.getDisplayThreshold() == 0).toList()); - return availableHints; - } - - private boolean isTaskSuccessfulInSubmission(ProgrammingExerciseTask task, Submission submission) { - var result = submission.getLatestResult(); - if (result == null || result.getFeedbacks().isEmpty()) { - return false; - } - var testCasesInTask = task.getTestCases(); - var feedbacks = result.getFeedbacks(); - // a task is successful if feedback for all in the task linked tests exist and if these tests all passed. - var feedbackForTask = testCasesInTask.stream().map(testCase -> findFeedbackForTestCase(testCase, feedbacks)).filter(Optional::isPresent).map(Optional::get).toList(); - if (feedbackForTask.size() != testCasesInTask.size()) { - // some expected test cases were not executed in the student's result - return false; - } - return feedbackForTask.stream().allMatch(feedback -> Boolean.TRUE.equals(feedback.isPositive())); - } - - /** - * Finds the first feedback in the provided list, that was created by the test case. - * - * @param testCase the test case to search the linked feedback for - * @return the feedback related by the test case, if present - */ - private Optional findFeedbackForTestCase(ProgrammingExerciseTestCase testCase, List feedbacks) { - return feedbacks.stream().filter(feedback -> testCase.equals(feedback.getTestCase())).findAny(); - } - - private List getSubmissionsForStudent(ProgrammingExercise exercise, User student) { - List submissions = new ArrayList<>(); - - var ratedStudentParticipation = studentParticipationRepository.findByExerciseIdAndStudentIdAndTestRunWithEagerSubmissionsResultsFeedbacksTestCases(exercise.getId(), - student.getId(), false); - ratedStudentParticipation.ifPresent(participation -> submissions.addAll(participation.getSubmissions())); - - var practiceStudentParticipation = studentParticipationRepository.findByExerciseIdAndStudentIdAndTestRunWithEagerSubmissionsResultsFeedbacksTestCases(exercise.getId(), - student.getId(), true); - practiceStudentParticipation.ifPresent(participation -> submissions.addAll(participation.getSubmissions())); - - submissions.sort(Comparator.reverseOrder()); - - return submissions; - } - - private int subsequentNumberOfSubmissionsForTaskWithStatus(List submissions, ProgrammingExerciseTask task, boolean successful) { - int subsequentNumberSuccessfulSubmissionsForTask = 0; - for (Submission submission : submissions) { - if (isTaskSuccessfulInSubmission(task, submission) != successful) { - break; - } - subsequentNumberSuccessfulSubmissionsForTask++; - } - return subsequentNumberSuccessfulSubmissionsForTask; - } - - /** - * Filter hints that meet the following conditions: - * 1. the previous task (if existing) is successful for at least the hint's threshold value - * 2. the current task is unsuccessful for at least the hint's threshold value - * - * @param subsequentNumberSuccessfulSubmissionsForPreviousTask the subsequent number of the latest submissions the previous task is successful - * @param subsequentNumberOfUnsuccessfulSubmissionsForCurrentTask the subsequent number of the latest submissions the current task is unsuccessful - * @param taskHints all exercise hints in current tasks - * @return the available exercise hints - */ - private Set getAvailableExerciseHintsForTask(Optional subsequentNumberSuccessfulSubmissionsForPreviousTask, - int subsequentNumberOfUnsuccessfulSubmissionsForCurrentTask, Set taskHints) { - Set availableHintsForTask = new HashSet<>(); - for (ExerciseHint hint : taskHints) { - // condition 1 - if (subsequentNumberSuccessfulSubmissionsForPreviousTask.isPresent() && subsequentNumberSuccessfulSubmissionsForPreviousTask.get() < hint.getDisplayThreshold()) { - continue; - } - - // condition 2 - if (subsequentNumberOfUnsuccessfulSubmissionsForCurrentTask >= hint.getDisplayThreshold()) { - availableHintsForTask.add(hint); - } - } - return availableHintsForTask; - } - - /** - * Returns the title of the hint identified by the given hint id if the exercise id stored in the hint matches the - * provided exercise id. - * - * @param exerciseId the exercise id that must match the one stored in the hint - * @param exerciseHintId the id of the hint - * @return the title of the hint if it was found; null otherwise - * @throws ConflictException if the provided exercise id does not match the one stored in the hint - */ - @Cacheable(cacheNames = "exerciseHintTitle", key = "''.concat(#exerciseId).concat('-').concat(#exerciseHintId)", unless = "#result == null") - public String getExerciseHintTitle(Long exerciseId, Long exerciseHintId) { - final var hint = exerciseHintRepository.findByIdElseThrow(exerciseHintId); - if (hint.getExercise() == null || !hint.getExercise().getId().equals(exerciseId)) { - throw new ConflictException("An exercise hint can only be retrieved if the exerciseIds match.", "exerciseHint", "exerciseIdsMismatch"); - } - - return hint.getTitle(); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/TestwiseCoverageService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/TestwiseCoverageService.java deleted file mode 100644 index 9fd6b6ca59f8..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/TestwiseCoverageService.java +++ /dev/null @@ -1,314 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import org.eclipse.jgit.api.errors.GitAPIException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; - -import de.tum.cit.aet.artemis.core.exception.InternalServerErrorException; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; -import de.tum.cit.aet.artemis.programming.domain.SolutionProgrammingExerciseParticipation; -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageFileReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageFileReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.TestwiseCoverageReportEntryRepository; -import de.tum.cit.aet.artemis.programming.service.GitService; -import de.tum.cit.aet.artemis.programming.service.RepositoryService; -import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.TestwiseCoverageReportDTO; - -/** - * Service for managing testwise coverage data and interacts with both CoverageReport, CoverageFileReport - * and TestwiseCoverageReportEntry - */ -@Profile(PROFILE_CORE) -@Service -public class TestwiseCoverageService { - - private static final Logger log = LoggerFactory.getLogger(TestwiseCoverageService.class); - - private final CoverageReportRepository coverageReportRepository; - - private final CoverageFileReportRepository coverageFileReportRepository; - - private final TestwiseCoverageReportEntryRepository testwiseCoverageReportEntryRepository; - - private final ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository; - - private final GitService gitService; - - private final RepositoryService repositoryService; - - public TestwiseCoverageService(CoverageReportRepository coverageReportRepository, CoverageFileReportRepository coverageFileReportRepository, - TestwiseCoverageReportEntryRepository testwiseCoverageReportEntryRepository, ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository, - RepositoryService repositoryService, GitService gitService) { - this.coverageReportRepository = coverageReportRepository; - this.coverageFileReportRepository = coverageFileReportRepository; - this.testwiseCoverageReportEntryRepository = testwiseCoverageReportEntryRepository; - this.programmingExerciseTestCaseRepository = programmingExerciseTestCaseRepository; - this.gitService = gitService; - this.repositoryService = repositoryService; - } - - /** - * Transforms the testwise coverage report DTOs to CoverageFileReports (without the test case attribute) mapped by the test case name. - * This method maps the file reports to primitive test case names because the test case is not present in the database - * on creating the entities from the DTOs. - * - * @param coverageReports the coverage report DTOs - * @return coverage file reports mapped by the test case name - */ - public Map> createTestwiseCoverageFileReportsWithoutTestsByTestCaseName(List coverageReports) { - Map> fileReportsByTestName = new HashMap<>(); - coverageReports.forEach(coveragePerTestDTO -> { - - // file reports for the current test case - Set testCoverageReports = new HashSet<>(); - for (var pathDTO : coveragePerTestDTO.getCoveredPathsPerTestDTOs()) { - - // the file reports for the current test case and path - Set fileCoverageReports = new HashSet<>(); - for (var fileDTO : pathDTO.getCoveredFilesPerTestDTOs()) { - var coverageEntriesPerFile = Arrays.stream(fileDTO.getCoveredLinesWithRanges().split(",")).map(optionalLineRange -> { - // The test case is not set for the report because it has not been saved yet to the database - var entry = new TestwiseCoverageReportEntry(); - // retrieve consecutive blocks from the ranged covered lines number - // Example: "2,3-6,7,9-30" - if (optionalLineRange.contains("-")) { - String[] range = optionalLineRange.split("-"); - int startLineNumber = Integer.parseInt(range[0]); - entry.setStartLine(startLineNumber); - entry.setLineCount(Integer.parseInt(range[1]) - startLineNumber + 1); - } - else { - entry.setStartLine(Integer.parseInt(optionalLineRange)); - entry.setLineCount(1); - } - return entry; - }).collect(Collectors.toSet()); - - // build the file report with the entries for this specific file - var fileReport = new CoverageFileReport(); - // 'src/' needs to be prepended to match the repositories' relative file path - String filePath = "src/" + pathDTO.getPath() + "/" + fileDTO.getFileName(); - fileReport.setFilePath(filePath); - fileReport.setTestwiseCoverageEntries(coverageEntriesPerFile); - fileCoverageReports.add(fileReport); - } - testCoverageReports.addAll(fileCoverageReports); - } - - // extract the test case name from the uniformPath - String[] split = coveragePerTestDTO.getUniformPath().split("/"); - String receivedTestCaseName = split[split.length - 1]; - fileReportsByTestName.put(receivedTestCaseName, testCoverageReports); - }); - - return fileReportsByTestName; - } - - /** - * Creates a coverage report from a testwise coverage report. - * Test case names are resolved to a test case of the given programming exercise, adds this reference to the given - * entries and saves the entries with the test case reference to the database. - * In case, no test case could be found for the given name, the report for this test case will not be saved - * - * @param fileReportByTestCaseName a map containing the test case name as a key and the file coverage reports without the - * reference to a test case as a value - * @param submission the solution programming submission for which the report is updated - * @param exercise the exercise for which the report should be updated - */ - public void createTestwiseCoverageReport(Map> fileReportByTestCaseName, ProgrammingExercise exercise, ProgrammingSubmission submission) { - // If the report already exists, do not create a new report. This is the case if the build plan will be re-run - boolean reportAlreadyExists = coverageReportRepository.existsBySubmissionId(submission.getId()); - if (reportAlreadyExists) { - return; - } - - var testCases = programmingExerciseTestCaseRepository.findByExerciseId(exercise.getId()); - var solutionLineCountByFilePath = getLineCountByFilePath(submission); - - // Save the full report with the test case and submission, but without the individual file reports as they do not have an ID yet - var fullReport = new CoverageReport(); - fullReport.setSubmission(submission); - var savedFullReport = coverageReportRepository.save(fullReport); - - // The file reports unique for a file path. the set is used to aggregate multiple file reports from multiple test - // cases into one file report - var uniqueFileReports = new HashSet(); - - fileReportByTestCaseName.forEach((testCaseName, fileReports) -> { - // retrieve the test matching the extracted test case name - var optionalTestCase = testCases.stream() - .filter(testCase -> testCaseName.equals(testCase.getTestName()) || testCaseName.replace("()", "").equals(testCase.getTestName())).findFirst(); - if (optionalTestCase.isEmpty()) { - log.error("No test case with name {} could be found when matching with the testwise coverage", testCaseName); - return; - } - var testCase = optionalTestCase.get(); - - fileReports.forEach(fileReport -> { - // If the file does not exist in the solution repository, no file report will be created - // This is for example the case if the test itself invokes code in a test class - if (solutionLineCountByFilePath.get(fileReport.getFilePath()) == null) { - return; - } - - // Temporarily save the testwise entries for the current file report as the entries do not have - // an ID and therefore cause an exception when the file report is saved to the database - var testwiseEntries = fileReport.getTestwiseCoverageEntries(); - - var optionalReportWithSameName = uniqueFileReports.stream().filter(report -> report.getFilePath().equals(fileReport.getFilePath())).findFirst(); - CoverageFileReport savedFileReport; - - if (optionalReportWithSameName.isEmpty()) { - // Remove the testwise entries temporarily as they do not have an ID yet - fileReport.setTestwiseCoverageEntries(Collections.emptySet()); - fileReport.setFullReport(savedFullReport); - // Save the line count - var lineCount = solutionLineCountByFilePath.get(fileReport.getFilePath()); - fileReport.setLineCount(lineCount); - - savedFileReport = coverageFileReportRepository.save(fileReport); - } - else { - var reportWithSameName = optionalReportWithSameName.get(); - savedFileReport = coverageFileReportRepository.save(reportWithSameName); - } - uniqueFileReports.add(savedFileReport); - - // Save all entries for the current file report to the database - testwiseEntries.forEach(entry -> { - entry.setTestCase(testCase); - entry.setFileReport(savedFileReport); - testwiseCoverageReportEntryRepository.save(entry); - }); - }); - }); - - var updatedFullReport = coverageReportRepository.findCoverageReportByIdWithEagerFileReportsAndEntriesElseThrow(savedFullReport.getId()); - // Calculate the unique line count for all file reports and save this value to the database - var coveredLinesCountByFilePath = calculateAndSaveUniqueLineCountsByFilePath(updatedFullReport); - - // Calculate the aggregated covered line ratio over all files - double aggregatedCoveredLineRatio = calculateAggregatedLineCoverage(solutionLineCountByFilePath, coveredLinesCountByFilePath); - updatedFullReport.setCoveredLineRatio(aggregatedCoveredLineRatio); - coverageReportRepository.save(updatedFullReport); - } - - /** - * Returns the line count by file name for all files in the solution repository for the last submission. - * - * @return line count by file name of files in the last submission's solution repository - */ - private Map getLineCountByFilePath(ProgrammingSubmission submission) { - try { - var solutionParticipation = (SolutionProgrammingExerciseParticipation) submission.getParticipation(); - var solutionRepo = gitService.getOrCheckoutRepository(solutionParticipation.getVcsRepositoryUri(), true); - gitService.resetToOriginHead(solutionRepo); - gitService.pullIgnoreConflicts(solutionRepo); - var solutionFiles = repositoryService.getFilesContentFromWorkingCopy(solutionRepo); - var result = new HashMap(); - solutionFiles.forEach((filePath, value) -> { - // do not count lines for non-java/kotlin files - if (!(filePath.endsWith(".java") || filePath.endsWith(".kt"))) { - return; - } - var lineCount = value.split("\n").length + 1; - result.put(filePath, lineCount); - }); - return result; - } - catch (GitAPIException e) { - log.error("Exception while generating testwise coverage report", e); - throw new InternalServerErrorException("Error while generating testwise coverage report: " + e.getMessage()); - } - } - - /** - * Calculates the aggregated covered line ratio for all file reports. - * - * @param lineCountByFileName the general line count by file name - * @param coveredLineCountByFileName the covered line count by file name - * @return the covered line ratio for all files - */ - private double calculateAggregatedLineCoverage(Map lineCountByFileName, Map coveredLineCountByFileName) { - var aggregatedLineCount = lineCountByFileName.values().stream().mapToInt(Integer::intValue).sum(); - if (aggregatedLineCount == 0) { - return 0; - } - var aggregatedCoveredLineCount = coveredLineCountByFileName.values().stream().mapToInt(Integer::intValue).sum(); - - return aggregatedCoveredLineCount / (double) aggregatedLineCount; - } - - /** - * Calculate the unique covered line count for all file reports and save this value to the database for all - * individual file reports. CoverageFileReports can contain multiple TestwiseCoverageReportEntries referencing - * the same lines, but referencing a different test case. This mapping is still required, but simple summing may - * count the same covered lines multiple times. - * - * @param report the report for which the line counts of its file reports should be calculated and saved - * @return a map with the number of covered lines (value) by file path (key) - */ - private Map calculateAndSaveUniqueLineCountsByFilePath(CoverageReport report) { - var coveredLinesByFilePath = new HashMap(); - report.getFileReports().forEach(fileReport -> { - var lineSet = new HashSet(); - fileReport.getTestwiseCoverageEntries() - .forEach(entry -> lineSet.addAll(IntStream.rangeClosed(entry.getStartLine(), entry.getStartLine() + entry.getLineCount() - 1).boxed().toList())); - fileReport.setCoveredLineCount(lineSet.size()); - coverageFileReportRepository.save(fileReport); - coveredLinesByFilePath.put(fileReport.getFilePath(), lineSet.size()); - }); - return coveredLinesByFilePath; - } - - /** - * Return the test-wise coverage report for the latest solution submission for a programming exercise without the file reports. - * - * @param exerciseId the exercise id for which the latest coverage report should be retrieved - * @return an Optional of the test-wise coverage report for the latest solution submission without the file reports - * if a report exists for the latest submission, otherwise an empty Optional - */ - public Optional getCoverageReportForLatestSolutionSubmissionFromProgrammingExercise(long exerciseId) { - var reports = coverageReportRepository.getLatestCoverageReportsWithLegalSubmissionsForProgrammingExercise(exerciseId, Pageable.ofSize(1)); - if (reports.isEmpty()) { - return Optional.empty(); - } - return Optional.of(reports.getFirst()); - } - - /** - * Return the full test-wise coverage report for the latest solution submission for a programming exercise containing all file reports - * - * @param exerciseId the exercise id for which the latest coverage report should be retrieved - * @return an Optional of the full test-wise coverage report for the latest solution submission with all file reports - * if a report exists for the latest submission, otherwise an empty Optional - */ - public Optional getFullCoverageReportForLatestSolutionSubmissionFromProgrammingExercise(long exerciseId) { - var reports = coverageReportRepository.getLatestCoverageReportsForLegalSubmissionsForProgrammingExerciseWithEagerFileReportsAndEntries(exerciseId, Pageable.ofSize(1)); - if (reports.isEmpty()) { - return Optional.empty(); - } - return Optional.of(reports.getFirst()); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/BehavioralBlackboard.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/BehavioralBlackboard.java deleted file mode 100644 index dd1417117e6b..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/BehavioralBlackboard.java +++ /dev/null @@ -1,58 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral; - -import java.util.List; -import java.util.Map; - -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; - -/** - * The blackboard for creating SolutionEntries for behavioral test cases utilizing the git-diff and test-wise coverage report. - */ -public class BehavioralBlackboard { - - private final ProgrammingExerciseGitDiffReport gitDiffReport; - - private final CoverageReport coverageReport; - - private final Map solutionRepoFiles; - - private List groupedFiles; - - private List solutionEntries; - - public BehavioralBlackboard(ProgrammingExerciseGitDiffReport gitDiffReport, CoverageReport coverageReport, Map solutionRepoFiles) { - this.gitDiffReport = gitDiffReport; - this.coverageReport = coverageReport; - this.solutionRepoFiles = solutionRepoFiles; - } - - public ProgrammingExerciseGitDiffReport getGitDiffReport() { - return gitDiffReport; - } - - public CoverageReport getCoverageReport() { - return coverageReport; - } - - public Map getSolutionRepoFiles() { - return solutionRepoFiles; - } - - public List getSolutionEntries() { - return solutionEntries; - } - - public void setSolutionEntries(List solutionEntries) { - this.solutionEntries = solutionEntries; - } - - public List getGroupedFiles() { - return groupedFiles; - } - - public void setGroupedFiles(List groupedFiles) { - this.groupedFiles = groupedFiles; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/BehavioralSolutionEntryGenerationException.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/BehavioralSolutionEntryGenerationException.java deleted file mode 100644 index 118662864564..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/BehavioralSolutionEntryGenerationException.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral; - -/** - * Exception used for the generation of solution entries for behavioral test cases - * It is thrown if there was an error while generating the solution entries - */ -public class BehavioralSolutionEntryGenerationException extends Exception { - - private static final String MESSAGE_PREFIX = "Unable to generate behavioral solution entries: "; - - public BehavioralSolutionEntryGenerationException(String message) { - super(MESSAGE_PREFIX + message); - } - - public BehavioralSolutionEntryGenerationException(String message, Throwable cause) { - super(MESSAGE_PREFIX + message, cause); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/BehavioralTestCaseService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/BehavioralTestCaseService.java deleted file mode 100644 index 71df2fdc6690..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/BehavioralTestCaseService.java +++ /dev/null @@ -1,201 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import org.eclipse.jgit.api.errors.GitAPIException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; - -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; -import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.service.GitService; -import de.tum.cit.aet.artemis.programming.service.RepositoryService; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseGitDiffReportService; -import de.tum.cit.aet.artemis.programming.service.hestia.TestwiseCoverageService; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.AddUncoveredLinesAsPotentialCodeBlocks; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.BehavioralKnowledgeSource; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.CombineChangeBlocks; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.CreateCommonChangeBlocks; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.CreateSolutionEntries; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.DropRemovedGitDiffEntries; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.ExtractChangedLines; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.ExtractCoveredLines; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.FindCommonLines; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.GroupGitDiffAndCoverageEntriesByFilePathAndTestCase; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.InsertFileContents; - -/** - * Service for handling Solution Entries of behavioral Test Cases. - */ -@Profile(PROFILE_CORE) -@Service -public class BehavioralTestCaseService { - - private static final Logger log = LoggerFactory.getLogger(BehavioralTestCaseService.class); - - private final GitService gitService; - - private final RepositoryService repositoryService; - - private final TestwiseCoverageService testwiseCoverageService; - - private final ProgrammingExerciseTestCaseRepository testCaseRepository; - - private final ProgrammingExerciseSolutionEntryRepository solutionEntryRepository; - - private final ProgrammingExerciseGitDiffReportService programmingExerciseGitDiffReportService; - - private final SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseParticipationRepository; - - public BehavioralTestCaseService(GitService gitService, RepositoryService repositoryService, TestwiseCoverageService testwiseCoverageService, - ProgrammingExerciseTestCaseRepository testCaseRepository, ProgrammingExerciseSolutionEntryRepository solutionEntryRepository, - ProgrammingExerciseGitDiffReportService programmingExerciseGitDiffReportService, - SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseParticipationRepository) { - this.gitService = gitService; - this.repositoryService = repositoryService; - this.testCaseRepository = testCaseRepository; - this.testwiseCoverageService = testwiseCoverageService; - this.solutionEntryRepository = solutionEntryRepository; - this.programmingExerciseGitDiffReportService = programmingExerciseGitDiffReportService; - this.solutionProgrammingExerciseParticipationRepository = solutionProgrammingExerciseParticipationRepository; - } - - /** - * Generates the solution entries for all behavioral test cases of a programming exercise. - * This uses the git-diff report, the testwise coverage, and the test cases of the programming exercise and - * requires them to exist. - * Therefore, this method also requires the exercise to have testwiseCoverageEnabled set to true. - * - * @param programmingExercise The programming exercise - * @return The new behavioral solution entries - * @throws BehavioralSolutionEntryGenerationException If there was an error while generating the solution entries - */ - public List generateBehavioralSolutionEntries(ProgrammingExercise programmingExercise) throws BehavioralSolutionEntryGenerationException { - if (!programmingExercise.getBuildConfig().isTestwiseCoverageEnabled()) { - throw new BehavioralSolutionEntryGenerationException("This feature is only supported for Java Exercises with active Testwise Coverage"); - } - var testCases = testCaseRepository.findByExerciseIdWithSolutionEntriesAndActive(programmingExercise.getId(), true); - if (testCases.isEmpty()) { - throw new BehavioralSolutionEntryGenerationException("Test cases have not been received yet"); - } - var gitDiffReport = programmingExerciseGitDiffReportService.getOrCreateReportOfExercise(programmingExercise); - if (gitDiffReport == null) { - throw new BehavioralSolutionEntryGenerationException("Git-Diff Report has not been generated"); - } - - var coverageReport = testwiseCoverageService.getFullCoverageReportForLatestSolutionSubmissionFromProgrammingExercise(programmingExercise.getId()).orElse(null); - if (coverageReport == null) { - throw new BehavioralSolutionEntryGenerationException("Testwise coverage report has not been generated"); - } - log.info("Generating the behavioral solution entries for programming exercise {}", programmingExercise.getId()); - - var solutionRepoFiles = readSolutionRepo(programmingExercise); - - var blackboard = new BehavioralBlackboard(gitDiffReport, coverageReport, solutionRepoFiles); - applyKnowledgeSources(blackboard); - var newSolutionEntries = blackboard.getSolutionEntries(); - if (newSolutionEntries == null || newSolutionEntries.isEmpty()) { - throw new BehavioralSolutionEntryGenerationException("No solution entry was generated"); - } - // Remove temporary id before saving - for (ProgrammingExerciseSolutionEntry solutionEntry : newSolutionEntries) { - solutionEntry.setId(null); - } - newSolutionEntries = solutionEntryRepository.saveAll(newSolutionEntries); - - // Get all old solution entries - var oldSolutionEntries = newSolutionEntries.stream().map(ProgrammingExerciseSolutionEntry::getTestCase) - .map(testCase1 -> testCases.stream().filter(testCase2 -> Objects.equals(testCase1.getId(), testCase2.getId())).findFirst().orElse(null)).filter(Objects::nonNull) - .flatMap(testCase -> testCase.getSolutionEntries().stream()).distinct().toList(); - - // Save new solution entries - newSolutionEntries = solutionEntryRepository.saveAll(newSolutionEntries); - - // Delete old solution entries - solutionEntryRepository.deleteAll(oldSolutionEntries); - - log.info("{} behavioral solution entries for programming exercise {} have been generated", newSolutionEntries.size(), programmingExercise.getId()); - return newSolutionEntries; - } - - /** - * Utilizing the blackboard pattern this method creates the behavioral solution entries step by step. - * Look at the specific KnowledgeSources to learn more. - * - * @param blackboard The blackboard containing the base information - * @throws BehavioralSolutionEntryGenerationException If there was an error while generating the solution entries - */ - private void applyKnowledgeSources(BehavioralBlackboard blackboard) throws BehavioralSolutionEntryGenerationException { - // Create knowledge sources (Turning the formatter off to make the code more readable) - // @formatter:off - List behavioralKnowledgeSources = Arrays.asList( - new DropRemovedGitDiffEntries(blackboard), - new GroupGitDiffAndCoverageEntriesByFilePathAndTestCase(blackboard), - new ExtractCoveredLines(blackboard), - new ExtractChangedLines(blackboard), - new FindCommonLines(blackboard), - new CreateCommonChangeBlocks(blackboard), - new InsertFileContents(blackboard), - new AddUncoveredLinesAsPotentialCodeBlocks(blackboard), - new CombineChangeBlocks(blackboard), - new CreateSolutionEntries(blackboard) - ); - // @formatter:on - - boolean done = false; - int iterations = 0; - while (!done) { - boolean didChanges = false; - for (BehavioralKnowledgeSource behavioralKnowledgeSource : behavioralKnowledgeSources) { - if (behavioralKnowledgeSource.executeCondition()) { - log.debug("Executing knowledge source {}", behavioralKnowledgeSource.getClass().getSimpleName()); - didChanges = behavioralKnowledgeSource.executeAction() || didChanges; - } - } - done = !didChanges; - iterations++; - // Safeguard to prevent an infinite loop - if (iterations >= 200) { - throw new BehavioralSolutionEntryGenerationException("The creation of the solution entries got stuck and was cancelled"); - } - } - } - - /** - * Reads the contents of all files in the solution repository and returns them mapped with the file path as the key. - * - * @param programmingExercise The programming exercise - * @return The file path of each file mapped to their contents - * @throws BehavioralSolutionEntryGenerationException If there was an error while reading the solution repository - */ - private Map readSolutionRepo(ProgrammingExercise programmingExercise) throws BehavioralSolutionEntryGenerationException { - try { - log.debug("Reading the contents of the solution repository"); - var solutionParticipationOptional = solutionProgrammingExerciseParticipationRepository.findByProgrammingExerciseId(programmingExercise.getId()); - if (solutionParticipationOptional.isEmpty()) { - return Collections.emptyMap(); - } - var solutionParticipation = solutionParticipationOptional.get(); - var solutionRepo = gitService.getOrCheckoutRepository(solutionParticipation.getVcsRepositoryUri(), true); - - gitService.resetToOriginHead(solutionRepo); - gitService.pullIgnoreConflicts(solutionRepo); - - return repositoryService.getFilesContentFromWorkingCopy(solutionRepo); - } - catch (GitAPIException e) { - throw new BehavioralSolutionEntryGenerationException("Error while reading solution repository", e); - } - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/GroupedFile.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/GroupedFile.java deleted file mode 100644 index 62eab75c4b1f..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/GroupedFile.java +++ /dev/null @@ -1,150 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral; - -import java.util.Collection; -import java.util.Objects; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; - -/** - * One GroupedFile groups the {@link ProgrammingExerciseGitDiffEntry}s and {@link TestwiseCoverageReportEntry}s together - * that belong to the same file. For each {@link ProgrammingExerciseTestCase} that covered the file a separate GroupedFile exists. - */ -public class GroupedFile { - - // The path of the file - private final String filePath; - - // The test case that covered something in this file - private final ProgrammingExerciseTestCase testCase; - - // All changes between the template and solution repositories in this file - private final Set gitDiffEntries; - - // All coverage entries of the test case in this file - private final Set coverageReportEntries; - - // The content of this file - private String fileContent; - - // The lines of this file that were changed - private Set changedLines; - - // The lines of this file that were covered by the test case - private Set coveredLines; - - // The lines in this file that were both covered and changed - private SortedSet commonLines; - - // The changes in this file that should be included in the solution entries - private SortedSet commonChanges; - - public GroupedFile(String filePath, ProgrammingExerciseTestCase testCase, Set gitDiffEntries, - Set coverageReportEntries) { - this.filePath = filePath; - this.testCase = testCase; - this.gitDiffEntries = gitDiffEntries; - this.coverageReportEntries = coverageReportEntries; - } - - public String getFilePath() { - return filePath; - } - - public ProgrammingExerciseTestCase getTestCase() { - return testCase; - } - - public Set getGitDiffEntries() { - return gitDiffEntries; - } - - public Set getCoverageReportEntries() { - return coverageReportEntries; - } - - public String getFileContent() { - return fileContent; - } - - public void setFileContent(String fileContent) { - this.fileContent = fileContent; - } - - public Set getChangedLines() { - return changedLines; - } - - public void setChangedLines(Set changedLines) { - this.changedLines = changedLines; - } - - public Set getCoveredLines() { - return coveredLines; - } - - public void setCoveredLines(Set coveredLines) { - this.coveredLines = coveredLines; - } - - public SortedSet getCommonLines() { - return commonLines; - } - - public void setCommonLines(Collection commonLines) { - this.commonLines = new TreeSet<>(commonLines); - } - - public SortedSet getCommonChanges() { - return commonChanges; - } - - public void setCommonChanges(Collection commonChanges) { - this.commonChanges = new TreeSet<>(commonChanges); - } - - @Override - public int hashCode() { - return Objects.hash(filePath, testCase, gitDiffEntries, coverageReportEntries, fileContent, changedLines, coveredLines, commonLines, commonChanges); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - GroupedFile that = (GroupedFile) obj; - return Objects.equals(filePath, that.filePath) && Objects.equals(testCase, that.testCase) && Objects.equals(gitDiffEntries, that.gitDiffEntries) - && Objects.equals(coverageReportEntries, that.coverageReportEntries) && Objects.equals(fileContent, that.fileContent) - && Objects.equals(changedLines, that.changedLines) && Objects.equals(coveredLines, that.coveredLines) && Objects.equals(commonLines, that.commonLines) - && Objects.equals(commonChanges, that.commonChanges); - } - - public record ChangeBlock(SortedSet lines, boolean isPotential) implements Comparable { - - public ChangeBlock(Collection lines) { - this(new TreeSet<>(lines), false); - } - - public ChangeBlock(Collection lines, boolean isPotential) { - this(new TreeSet<>(lines), isPotential); - } - - @Override - public int compareTo(GroupedFile.ChangeBlock other) { - return Integer.compare(this.lines.first(), other.lines.first()); - } - - public boolean intersectsOrTouches(ChangeBlock other) { - return (this.lines.first() > other.lines.first() && this.lines.first() <= other.lines.last() + 1) - || (this.lines.first() < other.lines.first() && this.lines.last() >= other.lines.first() - 1); - } - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/AddUncoveredLinesAsPotentialCodeBlocks.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/AddUncoveredLinesAsPotentialCodeBlocks.java deleted file mode 100644 index 3bacd3966662..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/AddUncoveredLinesAsPotentialCodeBlocks.java +++ /dev/null @@ -1,110 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource; - -import java.util.TreeSet; -import java.util.regex.Pattern; - -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile.ChangeBlock; - -/** - * For each {@link GroupedFile}: - * There are certain lines that are not covered by Jacoco (which is used for the testwise coverage) that may still be - * relevant to the code. This includes for example the `else` expression and curly braces. - * For each {@link ChangeBlock}: - * Check if there are such lines before or after the ChangeBlock. If there are add the entire prefix/postfix as a - * potential ChangeBlock to the GroupedFile. - */ -public class AddUncoveredLinesAsPotentialCodeBlocks extends BehavioralKnowledgeSource { - - private static final Pattern CURLY_BRACES_PATTERN = Pattern.compile("\\s*[}{]\\s*"); - - private static final Pattern ELSE_PATTERN = Pattern.compile("\\s*}?\\s*else\\s*\\{?\\s*"); - - private static final Pattern EMPTY_LINE_PATTERN = Pattern.compile("\\s*"); - - public AddUncoveredLinesAsPotentialCodeBlocks(BehavioralBlackboard blackboard) { - super(blackboard); - } - - @Override - public boolean executeCondition() { - return blackboard.getGroupedFiles() != null && blackboard.getGroupedFiles().stream().noneMatch(groupedFile -> groupedFile.getFileContent() == null) - && blackboard.getGroupedFiles().stream().noneMatch(groupedFile -> groupedFile.getCommonChanges() == null); - } - - @Override - public boolean executeAction() { - boolean didChanges = false; - - for (GroupedFile groupedFile : blackboard.getGroupedFiles()) { - var newChangeBlocks = new TreeSet(); - - for (ChangeBlock commonChange : groupedFile.getCommonChanges()) { - var firstLine = commonChange.lines().first(); - var potentialPrefix = getPotentialPrefix(firstLine, groupedFile.getFileContent()); - if (potentialPrefix != null) { - newChangeBlocks.add(potentialPrefix); - } - var lastLine = commonChange.lines().last(); - var potentialPostfix = getPotentialPostfix(lastLine, groupedFile.getFileContent()); - if (potentialPostfix != null) { - newChangeBlocks.add(potentialPostfix); - } - } - - if (!newChangeBlocks.isEmpty()) { - groupedFile.getCommonChanges().addAll(newChangeBlocks); - didChanges = true; - } - } - - return didChanges; - } - - private ChangeBlock getPotentialPrefix(int firstLine, String fileContent) { - var potentialLines = new TreeSet(); - var lineContents = fileContent.split("\n"); - // Starting two lines before the first line, as first line is the line in the file which starts at 1 and not 0 - for (int i = firstLine - 2; i >= 0; i--) { - var lineContent = lineContents[i]; - if (doesLineMatch(lineContent)) { - potentialLines.add(i + 1); - } - else { - break; - } - } - if (potentialLines.isEmpty()) { - return null; - } - else { - return new ChangeBlock(potentialLines, true); - } - } - - private ChangeBlock getPotentialPostfix(int lastLine, String fileContent) { - var potentialLines = new TreeSet(); - var lineContents = fileContent.split("\n"); - // Starting at last line instead of lastLine + 1, as last line is the line in the file which starts at 1 and not 0 - for (int i = lastLine; i < lineContents.length; i++) { - var lineContent = lineContents[i]; - if (doesLineMatch(lineContent)) { - potentialLines.add(i + 1); - } - else { - break; - } - } - if (potentialLines.isEmpty()) { - return null; - } - else { - return new ChangeBlock(potentialLines, true); - } - } - - private boolean doesLineMatch(String lineContent) { - return CURLY_BRACES_PATTERN.matcher(lineContent).matches() || ELSE_PATTERN.matcher(lineContent).matches() || EMPTY_LINE_PATTERN.matcher(lineContent).matches(); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/BehavioralKnowledgeSource.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/BehavioralKnowledgeSource.java deleted file mode 100644 index a96c534a2623..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/BehavioralKnowledgeSource.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource; - -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralSolutionEntryGenerationException; - -public abstract class BehavioralKnowledgeSource { - - protected final BehavioralBlackboard blackboard; - - public BehavioralKnowledgeSource(BehavioralBlackboard blackboard) { - this.blackboard = blackboard; - } - - /** - * Checks if the knowledge source can be applied - * - * @return true if the knowledge source can be applied - */ - public abstract boolean executeCondition(); - - /** - * Applies this knowledge source to the blackboard - * - * @return true if changes were made - */ - public abstract boolean executeAction() throws BehavioralSolutionEntryGenerationException; -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/CombineChangeBlocks.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/CombineChangeBlocks.java deleted file mode 100644 index d7f1b0c8f764..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/CombineChangeBlocks.java +++ /dev/null @@ -1,61 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource; - -import java.util.ArrayList; -import java.util.TreeSet; - -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; - -/** - * For each {@link GroupedFile}: - * Combines the {@link GroupedFile.ChangeBlock}s that have intersecting or directly attaching lines. - * E.g. if you have the block with the lines `1, 2, 3` and another with the lines `4, 5, 6`, these would be combined - * into one block with the lines `1, 2, 3, 4, 5, 6`. - */ -public class CombineChangeBlocks extends BehavioralKnowledgeSource { - - public CombineChangeBlocks(BehavioralBlackboard blackboard) { - super(blackboard); - } - - @Override - public boolean executeCondition() { - return blackboard.getGroupedFiles() != null && blackboard.getGroupedFiles().stream().noneMatch(groupedFile -> groupedFile.getCommonChanges() == null); - } - - @Override - public boolean executeAction() { - boolean didChanges = false; - - for (GroupedFile groupedFile : blackboard.getGroupedFiles()) { - while (true) { - var currentChangeBlocks = new ArrayList<>(groupedFile.getCommonChanges()); - var newChangeBlocks = new ArrayList(); - for (int i = 0; i < currentChangeBlocks.size(); i++) { - var currentChangeBlock = currentChangeBlocks.get(i); - // If this is not the last change block check if it can be merged with the next change block - if (i < currentChangeBlocks.size() - 1) { - var nextChangeBlock = currentChangeBlocks.get(i + 1); - if (currentChangeBlock.intersectsOrTouches(nextChangeBlock)) { - var lines = new TreeSet<>(currentChangeBlock.lines()); - lines.addAll(nextChangeBlock.lines()); - newChangeBlocks.add(new GroupedFile.ChangeBlock(lines)); - // Skip the next change block, as it has already been processed - i++; - continue; - } - } - newChangeBlocks.add(currentChangeBlock); - } - if (!newChangeBlocks.equals(currentChangeBlocks)) { - groupedFile.setCommonChanges(newChangeBlocks); - didChanges = true; - } - else { - break; - } - } - } - return didChanges; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/CreateCommonChangeBlocks.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/CreateCommonChangeBlocks.java deleted file mode 100644 index 5da770d0d9bb..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/CreateCommonChangeBlocks.java +++ /dev/null @@ -1,64 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource; - -import java.util.TreeSet; -import java.util.stream.IntStream; - -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; - -/** - * For each {@link GroupedFile}: - * Takes the common lines (intersection of covered and changed lines) and creates {@link GroupedFile.ChangeBlock}s - * from them. Each ChangeBlock represents one continuous block of common lines. - */ -public class CreateCommonChangeBlocks extends BehavioralKnowledgeSource { - - public CreateCommonChangeBlocks(BehavioralBlackboard blackboard) { - super(blackboard); - } - - @Override - public boolean executeCondition() { - return blackboard.getGroupedFiles() != null && blackboard.getGroupedFiles().stream().noneMatch(groupedFile -> groupedFile.getCommonLines() == null) - && blackboard.getGroupedFiles().stream().anyMatch(groupedFile -> groupedFile.getCommonChanges() == null); - } - - @Override - public boolean executeAction() { - boolean didChanges = false; - - for (GroupedFile groupedFile : blackboard.getGroupedFiles()) { - var changeBlocks = new TreeSet(); - - Integer previousLine = null; - Integer startLine = null; - int lineCount = 0; - - for (Integer currentLine : groupedFile.getCommonLines()) { - if (startLine == null) { - startLine = currentLine; - } - // Check if this is a new change block - if (previousLine != null && currentLine - 1 > previousLine) { - changeBlocks.add(new GroupedFile.ChangeBlock(IntStream.range(startLine, startLine + lineCount).boxed().toList())); - lineCount = 0; - startLine = currentLine; - } - lineCount++; - previousLine = currentLine; - } - - // Add the last change block if any existA - if (startLine != null) { - changeBlocks.add(new GroupedFile.ChangeBlock(IntStream.range(startLine, startLine + lineCount).boxed().toList())); - } - - if (!changeBlocks.equals(groupedFile.getCommonChanges())) { - groupedFile.setCommonChanges(changeBlocks); - didChanges = true; - } - } - - return didChanges; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/CreateSolutionEntries.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/CreateSolutionEntries.java deleted file mode 100644 index 90219039cbb1..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/CreateSolutionEntries.java +++ /dev/null @@ -1,57 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.stream.Collectors; - -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; - -/** - * For each {@link GroupedFile}: - * Creates one {@link ProgrammingExerciseSolutionEntry} for each non-potential {@link GroupedFile.ChangeBlock} - */ -public class CreateSolutionEntries extends BehavioralKnowledgeSource { - - public CreateSolutionEntries(BehavioralBlackboard blackboard) { - super(blackboard); - } - - @Override - public boolean executeCondition() { - return blackboard.getGroupedFiles() != null && blackboard.getGroupedFiles().stream().noneMatch(groupedFile -> groupedFile.getCommonChanges() == null); - } - - @Override - public boolean executeAction() { - var solutionEntries = new ArrayList(); - for (GroupedFile groupedFile : blackboard.getGroupedFiles()) { - for (GroupedFile.ChangeBlock changeBlock : groupedFile.getCommonChanges()) { - if (!changeBlock.isPotential()) { - solutionEntries.add(createSolutionEntry(groupedFile, changeBlock)); - } - } - } - if (solutionEntries.equals(blackboard.getSolutionEntries())) { - return false; - } - blackboard.setSolutionEntries(solutionEntries); - return !blackboard.getSolutionEntries().isEmpty(); - } - - private ProgrammingExerciseSolutionEntry createSolutionEntry(GroupedFile groupedFile, GroupedFile.ChangeBlock changeBlock) { - var solutionEntry = new ProgrammingExerciseSolutionEntry(); - // Set temporary id, as equals checks won't work otherwise - solutionEntry.setId(0L); - solutionEntry.setLine(changeBlock.lines().first()); - solutionEntry.setFilePath(groupedFile.getFilePath()); - solutionEntry.setTestCase(groupedFile.getTestCase()); - var fileContent = groupedFile.getFileContent(); - if (fileContent != null) { - var code = Arrays.stream(fileContent.split("\n")).skip(changeBlock.lines().first() - 1).limit(changeBlock.lines().size()).collect(Collectors.joining("\n")); - solutionEntry.setCode(code); - } - return solutionEntry; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/DropRemovedGitDiffEntries.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/DropRemovedGitDiffEntries.java deleted file mode 100644 index 679565d2c0e6..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/DropRemovedGitDiffEntries.java +++ /dev/null @@ -1,35 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource; - -import java.util.HashSet; -import java.util.stream.Collectors; - -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; - -/** - * Remove all {@link ProgrammingExerciseGitDiffEntry} from the - * {@link ProgrammingExerciseGitDiffReport} of the {@link BehavioralBlackboard} - * that represents consecutive blocks of removed code. - * Entries cannot be generated for removed code, therefore we have to drop them from the git diff report of the blackboard. - */ -public class DropRemovedGitDiffEntries extends BehavioralKnowledgeSource { - - public DropRemovedGitDiffEntries(BehavioralBlackboard blackboard) { - super(blackboard); - } - - @Override - public boolean executeCondition() { - return blackboard.getGitDiffReport() != null && blackboard.getGitDiffReport().getEntries() != null - && blackboard.getGitDiffReport().getEntries().stream().anyMatch(entry -> entry.getStartLine() == null || entry.getLineCount() == null); - } - - @Override - public boolean executeAction() { - var nonRemovedEntries = blackboard.getGitDiffReport().getEntries().stream().filter(entry -> entry.getStartLine() != null && entry.getLineCount() != null) - .collect(Collectors.toCollection(HashSet::new)); - blackboard.getGitDiffReport().setEntries(nonRemovedEntries); - return !nonRemovedEntries.isEmpty(); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/ExtractChangedLines.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/ExtractChangedLines.java deleted file mode 100644 index 3b259d17c21c..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/ExtractChangedLines.java +++ /dev/null @@ -1,35 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource; - -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; - -/** - * For each {@link GroupedFile}: - * Extracts the lines that were changed in the file (of the GroupedFile) from the - * {@link ProgrammingExerciseGitDiffEntry}s - */ -public class ExtractChangedLines extends BehavioralKnowledgeSource { - - public ExtractChangedLines(BehavioralBlackboard blackboard) { - super(blackboard); - } - - @Override - public boolean executeCondition() { - return blackboard.getGroupedFiles() != null && blackboard.getGroupedFiles().stream().anyMatch(groupedFile -> groupedFile.getChangedLines() == null); - } - - @Override - public boolean executeAction() { - for (GroupedFile groupedFile : blackboard.getGroupedFiles()) { - groupedFile.setChangedLines(groupedFile.getGitDiffEntries().stream() - .flatMapToInt(gitDiffEntry -> IntStream.range(gitDiffEntry.getStartLine(), gitDiffEntry.getStartLine() + gitDiffEntry.getLineCount())).boxed() - .collect(Collectors.toSet())); - } - return !blackboard.getGroupedFiles().stream().allMatch(groupedFile -> groupedFile.getChangedLines().isEmpty()); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/ExtractCoveredLines.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/ExtractCoveredLines.java deleted file mode 100644 index 19894d567205..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/ExtractCoveredLines.java +++ /dev/null @@ -1,36 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource; - -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; - -/** - * For each {@link GroupedFile}: - * Extracts the lines that were covered by the test case in the file (both of the GroupedFile) from the - * {@link TestwiseCoverageReportEntry}s - */ -public class ExtractCoveredLines extends BehavioralKnowledgeSource { - - public ExtractCoveredLines(BehavioralBlackboard blackboard) { - super(blackboard); - } - - @Override - public boolean executeCondition() { - return blackboard.getGroupedFiles() != null && blackboard.getGroupedFiles().stream().anyMatch(groupedFile -> groupedFile.getCoveredLines() == null); - } - - @Override - public boolean executeAction() { - for (GroupedFile groupedFile : blackboard.getGroupedFiles()) { - groupedFile.setCoveredLines(groupedFile.getCoverageReportEntries().stream() - .flatMapToInt( - coverageReportEntry -> IntStream.range(coverageReportEntry.getStartLine(), coverageReportEntry.getStartLine() + coverageReportEntry.getLineCount())) - .boxed().collect(Collectors.toSet())); - } - return !blackboard.getGroupedFiles().stream().allMatch(groupedFile -> groupedFile.getCoveredLines().isEmpty()); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/FindCommonLines.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/FindCommonLines.java deleted file mode 100644 index cf90f0b9af83..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/FindCommonLines.java +++ /dev/null @@ -1,35 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource; - -import java.util.ArrayList; -import java.util.List; - -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; - -/** - * For each {@link GroupedFile}: - * Creates the intersection of the changed lines and the covered lines. - */ -public class FindCommonLines extends BehavioralKnowledgeSource { - - public FindCommonLines(BehavioralBlackboard blackboard) { - super(blackboard); - } - - @Override - public boolean executeCondition() { - return blackboard.getGroupedFiles() != null && blackboard.getGroupedFiles().stream().noneMatch(groupedFile -> groupedFile.getChangedLines() == null) - && blackboard.getGroupedFiles().stream().noneMatch(groupedFile -> groupedFile.getCoveredLines() == null) - && blackboard.getGroupedFiles().stream().anyMatch(groupedFile -> groupedFile.getCommonLines() == null); - } - - @Override - public boolean executeAction() { - for (GroupedFile groupedFile : blackboard.getGroupedFiles()) { - List commonLines = new ArrayList<>(groupedFile.getCoveredLines()); - commonLines.retainAll(groupedFile.getChangedLines()); - groupedFile.setCommonLines(commonLines); - } - return !blackboard.getGroupedFiles().stream().allMatch(groupedFile -> groupedFile.getCommonLines().isEmpty()); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/GroupGitDiffAndCoverageEntriesByFilePathAndTestCase.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/GroupGitDiffAndCoverageEntriesByFilePathAndTestCase.java deleted file mode 100644 index c04311732a2b..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/GroupGitDiffAndCoverageEntriesByFilePathAndTestCase.java +++ /dev/null @@ -1,70 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; -import java.util.stream.Collectors; - -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageFileReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; - -/** - * Knowledge source that takes care of creating the {@link GroupedFile}s used by all other knowledge sources. - * These GroupedFiles are created by grouping all coverage entries and git-diff entries together that belong the same file and test case. - */ -public class GroupGitDiffAndCoverageEntriesByFilePathAndTestCase extends BehavioralKnowledgeSource { - - public GroupGitDiffAndCoverageEntriesByFilePathAndTestCase(BehavioralBlackboard blackboard) { - super(blackboard); - } - - @Override - public boolean executeCondition() { - return blackboard.getGroupedFiles() == null; - } - - @Override - public boolean executeAction() { - var gitDiffEntriesPerFile = blackboard.getGitDiffReport().getEntries().stream() - .collect(Collectors.toMap(ProgrammingExerciseGitDiffEntry::getFilePath, Collections::singleton, (set1, set2) -> { - var entries = new HashSet<>(set1); - entries.addAll(set2); - return entries; - })); - var coverageEntriesPerFile = blackboard.getCoverageReport().getFileReports().stream() - .collect(Collectors.toMap(CoverageFileReport::getFilePath, CoverageFileReport::getTestwiseCoverageEntries)); - - var commonFilePaths = new TreeSet<>(gitDiffEntriesPerFile.keySet()); - commonFilePaths.retainAll(coverageEntriesPerFile.keySet()); - - List groupedFiles = createGroupedFiles(gitDiffEntriesPerFile, coverageEntriesPerFile, commonFilePaths); - if (groupedFiles.isEmpty()) { - return false; - } - else { - blackboard.setGroupedFiles(groupedFiles); - return true; - } - } - - private List createGroupedFiles(Map> gitDiffEntriesPerFile, - Map> coverageEntriesPerFile, SortedSet commonFilePaths) { - return commonFilePaths.stream().flatMap(filePath -> { - var gitDiffEntries = gitDiffEntriesPerFile.get(filePath); - var coverageReportEntries = coverageEntriesPerFile.get(filePath); - return coverageReportEntries.stream().collect(Collectors.toMap(TestwiseCoverageReportEntry::getTestCase, Collections::singleton, (set1, set2) -> { - var entries = new HashSet<>(set1); - entries.addAll(set2); - return entries; - })).entrySet().stream().map(entry -> new GroupedFile(filePath, entry.getKey(), gitDiffEntries, entry.getValue())); - }).toList(); - } - -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/InsertFileContents.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/InsertFileContents.java deleted file mode 100644 index 43296df40286..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/behavioral/knowledgesource/InsertFileContents.java +++ /dev/null @@ -1,34 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource; - -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralSolutionEntryGenerationException; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; - -/** - * For each {@link GroupedFile}: - * Inserts the contents of the file into the GroupedFile using the filePath. - */ -public class InsertFileContents extends BehavioralKnowledgeSource { - - public InsertFileContents(BehavioralBlackboard blackboard) { - super(blackboard); - } - - @Override - public boolean executeCondition() { - return blackboard.getGroupedFiles() != null && blackboard.getGroupedFiles().stream().anyMatch(groupedFile -> groupedFile.getFileContent() == null); - } - - @Override - public boolean executeAction() throws BehavioralSolutionEntryGenerationException { - for (GroupedFile groupedFile : blackboard.getGroupedFiles()) { - var fileContent = blackboard.getSolutionRepoFiles().get(groupedFile.getFilePath()); - if (fileContent == null) { - throw new BehavioralSolutionEntryGenerationException( - String.format("Unable to find file '%s' in the solution repo despite it being referenced in the git-diff and coverage", groupedFile.getFilePath())); - } - groupedFile.setFileContent(fileContent); - } - return !blackboard.getGroupedFiles().isEmpty(); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralAttribute.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralAttribute.java deleted file mode 100644 index e1b776cf2b1e..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralAttribute.java +++ /dev/null @@ -1,79 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.structural; - -import java.util.ArrayList; -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.thoughtworks.qdox.model.JavaClass; -import com.thoughtworks.qdox.model.JavaField; - -/** - * Element of the test.json file representing the properties of an attribute of a class - * Used for the generation of solution entries for structural test cases - */ -@JsonIgnoreProperties(ignoreUnknown = true) -class StructuralAttribute implements StructuralElement { - - @JsonProperty(required = true) - private String name; - - @JsonProperty(required = true) - private String type; - - private List modifiers = new ArrayList<>(); - - private List annotations = new ArrayList<>(); - - @Override - public String getSourceCode(StructuralClassElements structuralClassElements, JavaClass solutionClass) { - JavaField solutionAttribute = getSolutionAttribute(solutionClass); - String attributeCode = ""; - if (!this.getAnnotations().isEmpty()) { - attributeCode += getAnnotationsString(this.getAnnotations(), solutionAttribute); - } - if (!this.getModifiers().isEmpty()) { - attributeCode += formatModifiers(this.getModifiers()) + " "; - } - attributeCode += (solutionAttribute == null ? this.getType() : solutionAttribute.getType().getGenericValue()) + " "; - attributeCode += this.getName(); - attributeCode += ";"; - return attributeCode; - } - - private JavaField getSolutionAttribute(JavaClass solutionClass) { - return solutionClass == null ? null : solutionClass.getFields().stream().filter(field -> field.getName().equals(this.getName())).findFirst().orElse(null); - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public List getModifiers() { - return modifiers; - } - - public void setModifiers(List modifiers) { - this.modifiers = modifiers; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public List getAnnotations() { - return annotations; - } - - public void setAnnotations(List annotations) { - this.annotations = annotations; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralClass.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralClass.java deleted file mode 100644 index 4cea7c67c954..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralClass.java +++ /dev/null @@ -1,145 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.structural; - -import java.util.ArrayList; -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.thoughtworks.qdox.model.JavaClass; - -/** - * Element of the test.json file representing the properties of a class - * Used for the generation of solution entries for structural test cases - */ -@JsonIgnoreProperties(ignoreUnknown = true) -class StructuralClass implements StructuralElement { - - @JsonProperty(required = true) - private String name; - - @JsonProperty(value = "package", required = true) - private String packageName; - - private String superclass; - - private List modifiers = new ArrayList<>(); - - @JsonProperty(defaultValue = "false") - private boolean isInterface; - - @JsonProperty(defaultValue = "false") - private boolean isEnum; - - private List interfaces = new ArrayList<>(); - - private List annotations = new ArrayList<>(); - - @Override - public String getSourceCode(StructuralClassElements structuralClassElements, JavaClass solutionClass) { - String classSolutionCode = "package " + this.getPackageName() + ";\n\n"; - - if (!this.getAnnotations().isEmpty()) { - classSolutionCode += getAnnotationsString(this.getAnnotations(), solutionClass); - } - classSolutionCode += getClassHeaderCode(solutionClass); - // Class Body - classSolutionCode += "{\n"; - classSolutionCode += SINGLE_INDENTATION; - if (this.isEnum()) { - classSolutionCode += String.join(", ", structuralClassElements.getEnumValues()); - } - classSolutionCode += "\n}"; - - return classSolutionCode; - } - - private String getClassHeaderCode(JavaClass solutionClass) { - String classHeaderCode = ""; - if (!this.getModifiers().isEmpty()) { - classHeaderCode += formatModifiers(this.getModifiers()) + " "; - } - classHeaderCode += (this.isInterface() ? "interface" : (this.isEnum() ? "enum" : "class")) + " "; - classHeaderCode += this.getName(); - if (solutionClass != null && !solutionClass.getTypeParameters().isEmpty()) { - classHeaderCode += getGenericTypesString(solutionClass.getTypeParameters()); - } - classHeaderCode += " "; - classHeaderCode += getInheritanceCode(); - return classHeaderCode; - } - - private String getInheritanceCode() { - String inheritanceCode = ""; - if (this.getSuperclass() != null) { - inheritanceCode += "extends " + this.getSuperclass() + " "; - } - if (!this.getInterfaces().isEmpty()) { - inheritanceCode += "implements " + String.join(", ", this.getInterfaces()) + " "; - } - return inheritanceCode; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPackageName() { - return packageName; - } - - public void setPackageName(String packageName) { - this.packageName = packageName; - } - - public String getSuperclass() { - return superclass; - } - - public void setSuperclass(String superclass) { - this.superclass = superclass; - } - - public boolean isInterface() { - return isInterface; - } - - public void setIsInterface(boolean anInterface) { - isInterface = anInterface; - } - - public boolean isEnum() { - return isEnum; - } - - public void setIsEnum(boolean anEnum) { - isEnum = anEnum; - } - - public List getInterfaces() { - return interfaces; - } - - public void setInterfaces(List interfaces) { - this.interfaces = interfaces; - } - - public List getModifiers() { - return modifiers; - } - - public void setModifiers(List modifiers) { - this.modifiers = modifiers; - } - - public List getAnnotations() { - return annotations; - } - - public void setAnnotations(List annotations) { - this.annotations = annotations; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralClassElements.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralClassElements.java deleted file mode 100644 index a5d6535b5090..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralClassElements.java +++ /dev/null @@ -1,66 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.structural; - -import java.util.ArrayList; -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Root element of the test.json file - * Used for the generation of solution entries for structural test cases - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class StructuralClassElements { - - @JsonProperty(value = "class", required = true) - private StructuralClass structuralClass; - - private List methods = new ArrayList<>(); - - private List attributes = new ArrayList<>(); - - private List constructors = new ArrayList<>(); - - private List enumValues = new ArrayList<>(); - - public StructuralClass getStructuralClass() { - return structuralClass; - } - - public void setStructuralClass(StructuralClass structuralClass) { - this.structuralClass = structuralClass; - } - - public List getMethods() { - return methods; - } - - public void setMethods(List methods) { - this.methods = methods; - } - - public List getAttributes() { - return attributes; - } - - public void setAttributes(List attributes) { - this.attributes = attributes; - } - - public List getConstructors() { - return constructors; - } - - public void setConstructors(List constructors) { - this.constructors = constructors; - } - - public List getEnumValues() { - return enumValues; - } - - public void setEnumValues(List enumValues) { - this.enumValues = enumValues; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralConstructor.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralConstructor.java deleted file mode 100644 index 6f01793c4595..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralConstructor.java +++ /dev/null @@ -1,77 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.structural; - -import java.util.ArrayList; -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.thoughtworks.qdox.model.JavaClass; -import com.thoughtworks.qdox.model.JavaConstructor; - -/** - * Element of the test.json file representing the properties of a constructor of a class - * Used for the generation of solution entries for structural test cases - */ -@JsonIgnoreProperties(ignoreUnknown = true) -class StructuralConstructor implements StructuralElement { - - private List modifiers = new ArrayList<>(); - - private List parameters = new ArrayList<>(); - - private List annotations = new ArrayList<>(); - - @Override - public String getSourceCode(StructuralClassElements structuralClassElements, JavaClass solutionClass) { - JavaConstructor solutionConstructor = getSolutionConstructor(solutionClass); - String constructorSolutionCode = ""; - if (!this.getAnnotations().isEmpty()) { - constructorSolutionCode += getAnnotationsString(this.getAnnotations(), solutionConstructor); - } - if (!this.getModifiers().isEmpty()) { - constructorSolutionCode += formatModifiers(this.getModifiers()) + " "; - } - constructorSolutionCode += structuralClassElements.getStructuralClass().getName(); - constructorSolutionCode += generateParametersString(this.getParameters(), solutionConstructor) + " "; - constructorSolutionCode += "{\n" + SINGLE_INDENTATION + "\n}"; - return constructorSolutionCode; - } - - /** - * Extracts the parameters from a constructor - * - * @param solutionClass The QDox class instance - * @return The parameters of the constructor - */ - private JavaConstructor getSolutionConstructor(JavaClass solutionClass) { - if (solutionClass == null) { - return null; - } - return solutionClass.getConstructors().stream() - .filter(javaConstructor -> doParametersMatch(this.getParameters(), javaConstructor.getParameters(), solutionClass.getTypeParameters())).findFirst().stream() - .findFirst().orElse(null); - } - - public List getModifiers() { - return modifiers; - } - - public void setModifiers(List modifiers) { - this.modifiers = modifiers; - } - - public List getParameters() { - return parameters; - } - - public void setParameters(List parameters) { - this.parameters = parameters; - } - - public List getAnnotations() { - return annotations; - } - - public void setAnnotations(List annotations) { - this.annotations = annotations; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralElement.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralElement.java deleted file mode 100644 index c62e54e7c02b..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralElement.java +++ /dev/null @@ -1,135 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.structural; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import com.thoughtworks.qdox.model.JavaAnnotatedElement; -import com.thoughtworks.qdox.model.JavaClass; -import com.thoughtworks.qdox.model.JavaExecutable; -import com.thoughtworks.qdox.model.JavaGenericDeclaration; -import com.thoughtworks.qdox.model.JavaParameter; -import com.thoughtworks.qdox.model.JavaType; -import com.thoughtworks.qdox.model.JavaTypeVariable; - -public interface StructuralElement { - - String SINGLE_INDENTATION = " "; - - /** - * Generates well formatted Java code for a structural element - * - * @param structuralClassElements The elements of the class from the test.json - * @param solutionClass The class read by QDox - * @return The code for the element - */ - String getSourceCode(StructuralClassElements structuralClassElements, JavaClass solutionClass); - - /** - * Generates the code for annotations that are required by the test.json file. - * Annotations that are present in the source code, but not in the test.json file will be excluded. - * - * @param structuralAnnotations The annotation names from the test.json - * @param annotatedElement The annotated element (e.g. method) read by QDox - * @return The code for the annotations - */ - default String getAnnotationsString(List structuralAnnotations, JavaAnnotatedElement annotatedElement) { - if (annotatedElement == null) { - return String.join("\n", structuralAnnotations) + "\n"; - } - else { - Set structuralAnnotationsSet = new HashSet<>(structuralAnnotations); - return annotatedElement.getAnnotations().stream() - .filter(solutionAnnotation -> structuralAnnotationsSet.contains(solutionAnnotation.getType().getSimpleName()) - || "Override".equals(solutionAnnotation.getType().getSimpleName())) - .map(annotation -> annotation.getCodeBlock().replace(annotation.getType().getGenericCanonicalName(), annotation.getType().getSimpleName())) - .collect(Collectors.joining()); - } - } - - /** - * Creates the String representation of a generics declaration - * - * @param typeParameters The generic type parameters - * @return The String representation - */ - default String getGenericTypesString(List> typeParameters) { - return "<" + typeParameters.stream().map(JavaType::getGenericValue).map(type -> type.substring(1, type.length() - 1)).collect(Collectors.joining(", ")) + ">"; - } - - /** - * Formats the modifiers properly. - * Currently, it only removes the 'optional: ' tags and joins them together. - * - * @param modifiers The modifiers array - * @return The formatted modifiers - */ - default String formatModifiers(List modifiers) { - if (modifiers == null) { - return ""; - } - return modifiers.stream().map(modifier -> modifier.replace("optional: ", "")).collect(Collectors.joining(" ")); - } - - /** - * Checks if the parameters from the source files and those from the test.json match. - * This is used for methods and constructor parameters. - * Contains special handling for generics - * - * @param parameters The parameters from the test.json file - * @param solutionParameters The parameters from the source code - * @param genericDeclarations The current generic declarations - * @return false if any parameter does not match - */ - default boolean doParametersMatch(List parameters, List solutionParameters, List> genericDeclarations) { - if (parameters == null) { - return solutionParameters.isEmpty(); - } - if (parameters.size() != solutionParameters.size()) { - return false; - } - for (int i = 0; i < parameters.size(); i++) { - var typeMatches = parameters.get(i).equals(solutionParameters.get(i).getType().getValue()); - var isGeneric = false; - for (JavaTypeVariable type : genericDeclarations) { - if (type.getName().equals(solutionParameters.get(i).getType().getValue()) || (type.getName() + "[]").equals(solutionParameters.get(i).getType().getValue())) { - isGeneric = true; - break; - } - } - if (!typeMatches && !isGeneric) { - return false; - } - } - return true; - } - - /** - * Generates the string representing the source code of a parameter list - * - * @param parameterTypes The parameters from the test.json file - * @param javaExecutable The executable (e.g. method) read by QDox to take the parameters from - * @return The parameter source code - */ - default String generateParametersString(List parameterTypes, JavaExecutable javaExecutable) { - List solutionParameters = javaExecutable != null ? javaExecutable.getParameters() : Collections.emptyList(); - String result = "("; - if (parameterTypes != null) { - for (int i = 0; i < parameterTypes.size(); i++) { - if (solutionParameters.size() > i) { - // Use original parameter names - parameterTypes.set(i, solutionParameters.get(i).getType().getGenericValue() + " " + solutionParameters.get(i).getName()); - } - else { - // Use var[i] as a fallback - parameterTypes.set(i, parameterTypes.get(i) + " var" + i); - } - } - result += String.join(", ", parameterTypes); - } - result += ")"; - return result; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralMethod.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralMethod.java deleted file mode 100644 index 66fa6eed3bee..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralMethod.java +++ /dev/null @@ -1,140 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.structural; - -import java.util.ArrayList; -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.Lists; -import com.thoughtworks.qdox.model.JavaClass; -import com.thoughtworks.qdox.model.JavaMethod; - -/** - * Element of the test.json file representing the properties of a method of a class - * Used for the generation of solution entries for structural test cases - */ -@JsonIgnoreProperties(ignoreUnknown = true) -class StructuralMethod implements StructuralElement { - - @JsonProperty(required = true) - private String name; - - private List modifiers = new ArrayList<>(); - - private List parameters = new ArrayList<>(); - - private List annotations = new ArrayList<>(); - - @JsonProperty(defaultValue = "void") - private String returnType; - - @Override - public String getSourceCode(StructuralClassElements structuralClassElements, JavaClass solutionClass) { - JavaMethod solutionMethod = getSolutionMethod(solutionClass); - String methodSolutionCode = ""; - boolean isAbstract = this.getModifiers().contains("abstract"); - - if (!this.getAnnotations().isEmpty()) { - methodSolutionCode += getAnnotationsString(this.getAnnotations(), solutionMethod); - } - - methodSolutionCode += formatModifiers(structuralClassElements, isAbstract); - - // Generics - if (solutionMethod != null && !solutionMethod.getTypeParameters().isEmpty()) { - methodSolutionCode += getGenericTypesString(solutionMethod.getTypeParameters()) + " "; - } - - // Return type - methodSolutionCode += solutionMethod != null ? solutionMethod.getReturnType().getGenericValue() + " " : this.getReturnType() + " "; - // Name - methodSolutionCode += this.getName(); - // Parameters - methodSolutionCode += generateParametersString(this.getParameters(), solutionMethod); - // Body - methodSolutionCode += isAbstract ? ";" : " {\n" + SINGLE_INDENTATION + "\n}"; - - return methodSolutionCode; - } - - /** - * Formats the modifiers of this method. - * - * @param structuralClassElements The elements of the class from the test.json - * @param isAbstract Whether the method is abstract - * @return The modifiers as Java code - */ - private String formatModifiers(StructuralClassElements structuralClassElements, boolean isAbstract) { - var modifiers = Lists.newArrayList(this.getModifiers()); - // Adjust modifiers for interfaces - if (structuralClassElements.getStructuralClass().isInterface()) { - if (isAbstract) { - modifiers.remove("abstract"); - } - else { - modifiers.addFirst("default"); - } - } - if (!modifiers.isEmpty()) { - return formatModifiers(modifiers) + " "; - } - return ""; - } - - /** - * Finds the QDox method in a given class by its test.json specification - * - * @param solutionClass The QDox class instance - * @return The QDox method instance or null if not found - */ - private JavaMethod getSolutionMethod(JavaClass solutionClass) { - if (solutionClass == null) { - return null; - } - return solutionClass.getMethods().stream().filter(javaMethod -> javaMethod.getName().equals(this.getName())).filter(javaMethod -> { - var genericTypes = new ArrayList<>(solutionClass.getTypeParameters()); - genericTypes.addAll(javaMethod.getTypeParameters()); - return doParametersMatch(this.getParameters(), javaMethod.getParameters(), genericTypes); - }).findFirst().orElse(null); - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public List getModifiers() { - return modifiers; - } - - public void setModifiers(List modifiers) { - this.modifiers = modifiers; - } - - public List getParameters() { - return parameters; - } - - public void setParameters(List parameters) { - this.parameters = parameters; - } - - public List getAnnotations() { - return annotations; - } - - public void setAnnotations(List annotations) { - this.annotations = annotations; - } - - public String getReturnType() { - return returnType; - } - - public void setReturnType(String returnType) { - this.returnType = returnType; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralSolutionEntryGenerationException.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralSolutionEntryGenerationException.java deleted file mode 100644 index 3b9614b9e2aa..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralSolutionEntryGenerationException.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.structural; - -/** - * Exception used for the generation of solution entries for structural test cases - * It is thrown if there was an error while generating the solution entries - */ -public class StructuralSolutionEntryGenerationException extends Exception { - - private static final String MESSAGE_PREFIX = "Unable to generate structural solution entries: "; - - public StructuralSolutionEntryGenerationException(String message) { - super(MESSAGE_PREFIX + message); - } - - public StructuralSolutionEntryGenerationException(String message, Throwable cause) { - super(MESSAGE_PREFIX + message, cause); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralTestCaseService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralTestCaseService.java deleted file mode 100644 index 889405573cff..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/hestia/structural/StructuralTestCaseService.java +++ /dev/null @@ -1,254 +0,0 @@ -package de.tum.cit.aet.artemis.programming.service.hestia.structural; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.jgit.api.errors.GitAPIException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.thoughtworks.qdox.JavaProjectBuilder; -import com.thoughtworks.qdox.model.JavaClass; -import com.thoughtworks.qdox.model.JavaType; - -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.Repository; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTestCaseType; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; -import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.service.GitService; - -/** - * Service for handling Solution Entries of structural Test Cases. - */ -@Profile(PROFILE_CORE) -@Service -public class StructuralTestCaseService { - - private static final Logger log = LoggerFactory.getLogger(StructuralTestCaseService.class); - - private final GitService gitService; - - private final ProgrammingExerciseTestCaseRepository testCaseRepository; - - private final ProgrammingExerciseSolutionEntryRepository solutionEntryRepository; - - private final SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseParticipationRepository; - - public StructuralTestCaseService(GitService gitService, ProgrammingExerciseTestCaseRepository testCaseRepository, - ProgrammingExerciseSolutionEntryRepository solutionEntryRepository, - SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseParticipationRepository) { - this.gitService = gitService; - this.testCaseRepository = testCaseRepository; - this.solutionEntryRepository = solutionEntryRepository; - this.solutionProgrammingExerciseParticipationRepository = solutionProgrammingExerciseParticipationRepository; - } - - /** - * Generates the solution entries for all structural test cases of a programming exercise. - * This includes solution entries for classes, attributes, methods and constructors. - * - * @param programmingExercise The programming exercise - * @return an unmodifiable list of new structural solution entries - * @throws StructuralSolutionEntryGenerationException If there was an error while generating the solution entries - */ - public List generateStructuralSolutionEntries(ProgrammingExercise programmingExercise) throws StructuralSolutionEntryGenerationException { - log.debug("Generating the structural SolutionEntries for the following programmingExercise: {} {}", programmingExercise.getId(), programmingExercise.getProjectName()); - - var testCases = testCaseRepository.findByExerciseIdWithSolutionEntriesAndActive(programmingExercise.getId(), true); - testCases.removeIf(testCase -> testCase.getType() != ProgrammingExerciseTestCaseType.STRUCTURAL); - - // No test cases = no solution entries needed - if (testCases.isEmpty()) { - return Collections.emptyList(); - } - - // Checkout the solution and test repositories - Repository solutionRepository; - Repository testRepository; - try { - var solutionParticipation = solutionProgrammingExerciseParticipationRepository.findByProgrammingExerciseId(programmingExercise.getId()); - if (solutionParticipation.isEmpty()) { - return Collections.emptyList(); - } - solutionRepository = gitService.getOrCheckoutRepository(solutionParticipation.get().getVcsRepositoryUri(), true); - testRepository = gitService.getOrCheckoutRepository(programmingExercise.getVcsTestRepositoryUri(), true); - - gitService.resetToOriginHead(solutionRepository); - gitService.pullIgnoreConflicts(solutionRepository); - gitService.resetToOriginHead(testRepository); - gitService.pullIgnoreConflicts(testRepository); - } - catch (GitAPIException e) { - var error = "Error while checking out repositories"; - log.error(error, e); - throw new StructuralSolutionEntryGenerationException(error, e); - } - - var classElements = readStructureOracleFile(testRepository.getLocalPath()); - var solutionClasses = getClassesFromFiles(retrieveJavaSourceFiles(solutionRepository.getLocalPath())); - - // Create new solution entries - List newSolutionEntries = generateStructuralSolutionEntries(testCases, solutionRepository, classElements, solutionClasses); - - // Get all old solution entries - var oldSolutionEntries = newSolutionEntries.stream().map(ProgrammingExerciseSolutionEntry::getTestCase).flatMap(testCase -> testCase.getSolutionEntries().stream()) - .distinct().toList(); - - // Save new solution entries - newSolutionEntries = solutionEntryRepository.saveAll(newSolutionEntries); - - // Delete old solution entries - solutionEntryRepository.deleteAll(oldSolutionEntries); - - return newSolutionEntries; - } - - /** - * Private method that takes care of the actual generation of structural solution entries. - * - * @param testCases The test cases of the programming exercise - * @param solutionRepository The solution repository of the programming exercise - * @param classElements The entries from the test.json file - * @param solutionClasses The classes read with QDox - * @return an unmodifiable list of new structural solution entries - */ - private List generateStructuralSolutionEntries(Set testCases, Repository solutionRepository, - StructuralClassElements[] classElements, Map solutionClasses) { - return Arrays.stream(classElements).flatMap(classElement -> { - var packageName = classElement.getStructuralClass().getPackageName(); - var name = classElement.getStructuralClass().getName(); - var solutionClass = solutionClasses.get(packageName + "." + name); - String filePath = "src/" + packageName.replaceAll("\\.", "/") + "/" + name + ".java"; - if (solutionClass != null) { - try { - filePath = solutionRepository.getLocalPath().toAbsolutePath().toUri().relativize(solutionClass.getSource().getURL().toURI()).toString(); - } - catch (URISyntaxException e) { - log.warn("Unable to create file path for class", e); - } - } - - String classSolutionCode = classElement.getStructuralClass().getSourceCode(classElement, solutionClass); - List constructorsSolutionCode = classElement.getConstructors().stream().map(constructor -> constructor.getSourceCode(classElement, solutionClass)).toList(); - List methodsSolutionCode = classElement.getMethods().stream().map(method -> method.getSourceCode(classElement, solutionClass)).toList(); - List attributesSolutionCode = classElement.getAttributes().stream().map(attribute -> attribute.getSourceCode(classElement, solutionClass)).toList(); - return Stream.of(createSolutionEntry(filePath, classSolutionCode, findStructuralTestCase("Class", name, testCases)), - createSolutionEntry(filePath, String.join("\n\n", attributesSolutionCode), findStructuralTestCase("Attributes", name, testCases)), - createSolutionEntry(filePath, String.join("\n\n", constructorsSolutionCode), findStructuralTestCase("Constructors", name, testCases)), - createSolutionEntry(filePath, String.join("\n\n", methodsSolutionCode), findStructuralTestCase("Methods", name, testCases))); - }).filter(Objects::nonNull).toList(); - } - - /** - * Finds a structural test case of a specific type and class in the list of all test cases of an exercise. - * - * @param type The type of the structural test case (e.g. Class, Methods) - * @param className The name of the Class - * @param testCases The list of test cases - * @return The matching test case or empty in none found - */ - private Optional findStructuralTestCase(String type, String className, Set testCases) { - return testCases.stream().filter(testCase -> testCase.getTestName().equals("test" + type + "[" + className + "]")).findFirst(); - } - - /** - * Helper method for creating a solution entry. - * If the given test case is not present this will return null. - * - * @param filePath The filePath of the solution entry - * @param code The code of the solution entry - * @param testCase The test case of the solution entry - * @return A SolutionEntry if testCase is present otherwise null - */ - private ProgrammingExerciseSolutionEntry createSolutionEntry(String filePath, String code, Optional testCase) { - return testCase.map(actualTestCase -> { - var solutionEntry = new ProgrammingExerciseSolutionEntry(); - solutionEntry.setFilePath(filePath); - solutionEntry.setPreviousLine(null); - solutionEntry.setPreviousCode(null); - solutionEntry.setLine(1); - solutionEntry.setCode(code); - solutionEntry.setTestCase(actualTestCase); - return solutionEntry; - }).orElse(null); - } - - /** - * Finds, reads and parses the test.json file from the test repository - * - * @param testRepoPath The base path of the test repository - * @return The parsed test.json file - * @throws StructuralSolutionEntryGenerationException If the test.json does not exist or could not be read - */ - private StructuralClassElements[] readStructureOracleFile(Path testRepoPath) throws StructuralSolutionEntryGenerationException { - try (Stream files = Files.walk(testRepoPath)) { - var testJsonFile = files.filter(Files::isRegularFile).filter(path -> "test.json".equals(path.getFileName().toString())).findFirst(); - if (testJsonFile.isEmpty()) { - throw new StructuralSolutionEntryGenerationException("Unable to locate test.json"); - } - else { - String jsonContent = Files.readString(testJsonFile.get()); - var objectMapper = new ObjectMapper(); - return objectMapper.readValue(jsonContent, StructuralClassElements[].class); - } - } - catch (IOException e) { - throw new StructuralSolutionEntryGenerationException("Error while reading test.json", e); - } - } - - /** - * Collects all java source files in a given path. - * - * @param start The base path - * @return The paths to all java source files - * @throws StructuralSolutionEntryGenerationException If there was an IOException - */ - private List retrieveJavaSourceFiles(Path start) throws StructuralSolutionEntryGenerationException { - var matcher = FileSystems.getDefault().getPathMatcher("glob:**/*.java"); - try (Stream files = Files.walk(start)) { - return files.filter(Files::isRegularFile).filter(matcher::matches).toList(); - } - catch (IOException e) { - var error = "Could not retrieve the project files to generate the structural solution entries"; - log.error(error, e); - throw new StructuralSolutionEntryGenerationException(error, e); - } - } - - private Map getClassesFromFiles(List javaSourceFiles) throws StructuralSolutionEntryGenerationException { - JavaProjectBuilder builder = new JavaProjectBuilder(); - try { - for (Path source : javaSourceFiles) { - builder.addSource(source.toFile()); - } - } - catch (IOException e) { - var error = "Could not add java source to builder"; - log.error(error, e); - throw new StructuralSolutionEntryGenerationException(error, e); - } - return builder.getClasses().stream().collect(Collectors.toMap(JavaType::getFullyQualifiedName, clazz -> clazz)); - } -} 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 cca2995cdba0..6ad7b4c65aec 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 @@ -37,21 +37,21 @@ 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(C_PLUS_PLUS, new ProgrammingLanguageFeature(C_PLUS_PLUS, false, false, true, false, false, List.of(), false, false)); - programmingLanguageFeatures.put(C_SHARP, new ProgrammingLanguageFeature(C_SHARP, false, false, true, false, false, List.of(), false, false)); - programmingLanguageFeatures.put(GO, new ProgrammingLanguageFeature(GO, false, false, true, true, false, List.of(), false, false)); - programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, false, false, false, false, true, List.of(), false, false)); + programmingLanguageFeatures.put(EMPTY, new ProgrammingLanguageFeature(EMPTY, false, false, false, false, false, List.of(), false)); + programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, false, true, false, false, List.of(FACT, GCC), false)); + programmingLanguageFeatures.put(C_PLUS_PLUS, new ProgrammingLanguageFeature(C_PLUS_PLUS, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(C_SHARP, new ProgrammingLanguageFeature(C_SHARP, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(GO, new ProgrammingLanguageFeature(GO, false, false, true, true, false, List.of(), false)); + programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, false, false, false, false, true, List.of(), 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)); + new ProgrammingLanguageFeature(JAVA, true, true, true, true, false, List.of(PLAIN_GRADLE, GRADLE_GRADLE, PLAIN_MAVEN, MAVEN_MAVEN, MAVEN_BLACKBOX), true)); + programmingLanguageFeatures.put(JAVASCRIPT, new ProgrammingLanguageFeature(JAVASCRIPT, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(KOTLIN, new ProgrammingLanguageFeature(KOTLIN, true, false, true, true, false, List.of(), true)); + programmingLanguageFeatures.put(PYTHON, new ProgrammingLanguageFeature(PYTHON, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(R, new ProgrammingLanguageFeature(R, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), 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(TYPESCRIPT, new ProgrammingLanguageFeature(TYPESCRIPT, false, false, true, false, false, List.of(), false, false)); + programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, true, true, true, false, List.of(PLAIN), false)); + programmingLanguageFeatures.put(TYPESCRIPT, new ProgrammingLanguageFeature(TYPESCRIPT, false, false, true, false, false, List.of(), false)); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsResultService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsResultService.java index 9c27aa4c5733..2730c9e82f9a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsResultService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsResultService.java @@ -10,21 +10,17 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import de.tum.cit.aet.artemis.assessment.repository.FeedbackRepository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; -import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO; +import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification; import de.tum.cit.aet.artemis.programming.repository.BuildLogStatisticsEntryRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingSubmissionRepository; -import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseFeedbackCreationService; import de.tum.cit.aet.artemis.programming.service.ci.AbstractContinuousIntegrationResultService; import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.TestResultsDTO; -import de.tum.cit.aet.artemis.programming.service.hestia.TestwiseCoverageService; @Profile("jenkins") @Service @@ -32,15 +28,13 @@ public class JenkinsResultService extends AbstractContinuousIntegrationResultSer private static final Logger log = LoggerFactory.getLogger(JenkinsResultService.class); - public JenkinsResultService(ProgrammingSubmissionRepository programmingSubmissionRepository, FeedbackRepository feedbackRepository, BuildLogEntryService buildLogService, - BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, TestwiseCoverageService testwiseCoverageService, - ProgrammingExerciseFeedbackCreationService feedbackCreationService, ProgrammingExerciseTestCaseRepository testCaseRepository, - ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository) { - super(testCaseRepository, buildLogStatisticsEntryRepository, testwiseCoverageService, feedbackCreationService, programmingExerciseBuildConfigRepository); + public JenkinsResultService(BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, ProgrammingExerciseFeedbackCreationService feedbackCreationService, + ProgrammingExerciseTestCaseRepository testCaseRepository, ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository) { + super(testCaseRepository, buildLogStatisticsEntryRepository, feedbackCreationService, programmingExerciseBuildConfigRepository); } @Override - public AbstractBuildResultNotificationDTO convertBuildResult(Object requestBody) { + public BuildResultNotification convertBuildResult(Object requestBody) { return TestResultsDTO.convert(requestBody); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsService.java index 4a4e42f602a4..887bef4857b9 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsService.java @@ -30,9 +30,9 @@ import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; import de.tum.cit.aet.artemis.programming.domain.build.BuildPlanType; import de.tum.cit.aet.artemis.programming.dto.CheckoutDirectoriesDTO; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusTemplateService; -import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile; import de.tum.cit.aet.artemis.programming.service.ci.AbstractContinuousIntegrationService; import de.tum.cit.aet.artemis.programming.service.ci.CIPermission; import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.TestResultsDTO; 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 1264ba9b643f..c54b617d5546 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 @@ -48,12 +48,13 @@ import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.RepositoryType; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; +import de.tum.cit.aet.artemis.programming.dto.aeolus.AeolusRepository; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; +import de.tum.cit.aet.artemis.programming.dto.aeolus.WindfileMetadata; import de.tum.cit.aet.artemis.programming.repository.BuildPlanRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusBuildPlanService; -import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusRepository; -import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService; import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.TestResultsDTO; import de.tum.cit.aet.artemis.programming.service.jenkins.JenkinsEndpoints; @@ -287,7 +288,7 @@ private void postBuildPlanConfigChange(String buildPlanKey, String buildProjectK * @return the build plan key */ public String getBuildPlanKeyFromTestResults(TestResultsDTO testResultsDTO) throws JsonProcessingException { - final var nameParams = testResultsDTO.getFullName().split(" "); + final var nameParams = testResultsDTO.fullName().split(" "); /* * Jenkins gives the full name of a job as » E.g. the third build of an exercise (projectKey = TESTEXC) for its solution build * (TESTEXC-SOLUTION) would be: TESTEXC » TESTEXC-SOLUTION #3 ==> This would mean that at index 2, we have the actual job/plan key, i.e. TESTEXC-SOLUTION @@ -507,8 +508,9 @@ private String createCustomAeolusBuildPlanForExercise(ProgrammingExercise progra buildConfig.getBranch(), buildConfig.getCheckoutSolutionRepository(), repositoryUri, testRepositoryUri, solutionRepositoryUri, List.of()); String resultHookUrl = artemisServerUrl + NEW_RESULT_RESOURCE_API_PATH; - windfile.setPreProcessingMetadata(buildPlanId, programmingExercise.getProjectName(), this.vcsCredentials, resultHookUrl, "planDescription", repositories, - this.artemisAuthenticationTokenKey); + var metadata = new WindfileMetadata(programmingExercise.getProjectName(), buildPlanId, "planDescription", null, vcsCredentials, null, resultHookUrl, + artemisAuthenticationTokenKey); + windfile = new Windfile(windfile, metadata, repositories); String generatedKey = aeolusBuildPlanService.get().publishBuildPlan(windfile, AeolusTarget.JENKINS); if (generatedKey != null && generatedKey.contains("-")) { diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsPipelineScriptCreator.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsPipelineScriptCreator.java index 92fca529c68b..a18f3ea8ffc5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsPipelineScriptCreator.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsPipelineScriptCreator.java @@ -36,8 +36,6 @@ public class JenkinsPipelineScriptCreator extends AbstractBuildPlanCreator { private static final String REPLACE_IS_STATIC_CODE_ANALYSIS_ENABLED = "#isStaticCodeAnalysisEnabled"; - private static final String REPLACE_TESTWISE_COVERAGE = "#testWiseCoverage"; - private final ResourceLoaderService resourceLoaderService; private final ProgrammingLanguageConfiguration programmingLanguageConfiguration; @@ -58,8 +56,7 @@ protected String generateDefaultBuildPlan(final ProgrammingExercise exercise) { final String pipelineScript = loadPipelineScript(exercise, projectType); final boolean isStaticCodeAnalysisEnabled = exercise.isStaticCodeAnalysisEnabled(); - final boolean isTestwiseCoverageAnalysisEnabled = exercise.getBuildConfig().isTestwiseCoverageEnabled(); - final var replacements = getReplacements(programmingLanguage, projectType, isStaticCodeAnalysisEnabled, isTestwiseCoverageAnalysisEnabled); + final var replacements = getReplacements(programmingLanguage, projectType, isStaticCodeAnalysisEnabled); return replaceVariablesInBuildPlanTemplate(replacements, pipelineScript); } @@ -86,12 +83,10 @@ private String loadPipelineScript(final ProgrammingExercise exercise, final Opti } } - private Map getReplacements(final ProgrammingLanguage programmingLanguage, final Optional projectType, final boolean isStaticCodeAnalysisEnabled, - final boolean isTestwiseCoverageAnalysisEnabled) { + private Map getReplacements(final ProgrammingLanguage programmingLanguage, final Optional projectType, final boolean isStaticCodeAnalysisEnabled) { final Map replacements = new HashMap<>(); replacements.put(REPLACE_IS_STATIC_CODE_ANALYSIS_ENABLED, String.valueOf(isStaticCodeAnalysisEnabled)); - replacements.put(REPLACE_TESTWISE_COVERAGE, String.valueOf(isTestwiseCoverageAnalysisEnabled)); replacements.put(REPLACE_DOCKER_IMAGE_NAME, programmingLanguageConfiguration.getImage(programmingLanguage, projectType)); replacements.put(REPLACE_DOCKER_ARGS, String.join(" ", programmingLanguageConfiguration.getDefaultDockerFlags())); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIBuildConfigurationService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIBuildConfigurationService.java index e4803619c97d..498f416f61ed 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIBuildConfigurationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIBuildConfigurationService.java @@ -11,10 +11,10 @@ import de.tum.cit.aet.artemis.core.exception.LocalCIException; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseBuildConfig; +import de.tum.cit.aet.artemis.programming.dto.aeolus.ScriptAction; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; import de.tum.cit.aet.artemis.programming.service.BuildScriptProviderService; import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusTemplateService; -import de.tum.cit.aet.artemis.programming.service.aeolus.ScriptAction; -import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile; @Service @Profile(PROFILE_LOCALCI) @@ -57,18 +57,18 @@ public String createBuildScript(ProgrammingExercise programmingExercise) { windfile = aeolusTemplateService.getDefaultWindfileFor(programmingExercise); } if (windfile != null) { - actions = windfile.getScriptActions(); + actions = windfile.scriptActions(); } else { throw new LocalCIException("No windfile found for programming exercise " + programmingExercise.getId()); } actions.forEach(action -> { - String workdir = action.getWorkdir(); + String workdir = action.workdir(); if (workdir != null) { buildScriptBuilder.append("cd ").append(LOCALCI_WORKING_DIRECTORY).append("/testing-dir/").append(workdir).append("\n"); } - buildScriptBuilder.append(action.getScript()).append("\n"); + buildScriptBuilder.append(action.script()).append("\n"); if (workdir != null) { buildScriptBuilder.append("cd ").append(LOCALCI_WORKING_DIRECTORY).append("/testing-dir\n"); } 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 d764193795c9..b8478cf65daf 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 @@ -43,23 +43,23 @@ 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(C_PLUS_PLUS, new ProgrammingLanguageFeature(C_PLUS_PLUS, false, false, true, false, false, List.of(), false, true)); - programmingLanguageFeatures.put(C_SHARP, new ProgrammingLanguageFeature(C_SHARP, false, false, true, false, false, List.of(), false, true)); - programmingLanguageFeatures.put(GO, new ProgrammingLanguageFeature(GO, false, false, true, true, false, List.of(), false, true)); - programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, true, false, false, false, true, List.of(), false, true)); + programmingLanguageFeatures.put(EMPTY, new ProgrammingLanguageFeature(EMPTY, false, false, false, false, false, List.of(), false)); + programmingLanguageFeatures.put(ASSEMBLER, new ProgrammingLanguageFeature(ASSEMBLER, false, false, false, false, false, List.of(), false)); + programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, true, true, false, false, List.of(FACT, GCC), false)); + programmingLanguageFeatures.put(C_PLUS_PLUS, new ProgrammingLanguageFeature(C_PLUS_PLUS, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(C_SHARP, new ProgrammingLanguageFeature(C_SHARP, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(GO, new ProgrammingLanguageFeature(GO, false, false, true, true, false, List.of(), false)); + programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, true, false, false, false, true, List.of(), false)); 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(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(OCAML, new ProgrammingLanguageFeature(OCAML, false, false, false, false, true, List.of(), false, true)); - programmingLanguageFeatures.put(PYTHON, new ProgrammingLanguageFeature(PYTHON, false, true, 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(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, false, true, true, false, List.of(PLAIN), false, true)); - programmingLanguageFeatures.put(TYPESCRIPT, new ProgrammingLanguageFeature(TYPESCRIPT, false, false, true, false, false, List.of(), false, true)); - programmingLanguageFeatures.put(VHDL, new ProgrammingLanguageFeature(VHDL, false, false, false, false, false, List.of(), false, true)); + new ProgrammingLanguageFeature(JAVA, true, true, true, true, false, List.of(PLAIN_GRADLE, GRADLE_GRADLE, PLAIN_MAVEN, MAVEN_MAVEN), false)); + programmingLanguageFeatures.put(JAVASCRIPT, new ProgrammingLanguageFeature(JAVASCRIPT, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(KOTLIN, new ProgrammingLanguageFeature(KOTLIN, false, false, true, true, false, List.of(), false)); + programmingLanguageFeatures.put(OCAML, new ProgrammingLanguageFeature(OCAML, false, false, false, false, true, List.of(), false)); + programmingLanguageFeatures.put(PYTHON, new ProgrammingLanguageFeature(PYTHON, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(R, new ProgrammingLanguageFeature(R, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, false, true, true, false, List.of(PLAIN), false)); + programmingLanguageFeatures.put(TYPESCRIPT, new ProgrammingLanguageFeature(TYPESCRIPT, false, false, true, false, false, List.of(), false)); + programmingLanguageFeatures.put(VHDL, new ProgrammingLanguageFeature(VHDL, false, false, false, false, false, List.of(), false)); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIQueueWebsocketService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIQueueWebsocketService.java index 872057e95145..f82777abddb2 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIQueueWebsocketService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIQueueWebsocketService.java @@ -182,8 +182,8 @@ private static List removeUnnecessaryInformation(List getTestResultPaths(Windfile windfile) throws IllegalArgumentException { - List testResultPaths = new ArrayList<>(); - for (AeolusResult testResultPath : windfile.getResults()) { - testResultPaths.add(LOCALCI_WORKING_DIRECTORY + "/testing-dir/" + testResultPath.path()); - } - return testResultPaths; + private List getTestResultPaths(Windfile windfile) { + return windfile.results().stream().map(result -> LOCALCI_WORKING_DIRECTORY + "/testing-dir/" + result.path()).toList(); } /** @@ -319,13 +313,12 @@ private BuildConfig getBuildConfig(ProgrammingExerciseParticipation participatio ProjectType projectType = programmingExercise.getProjectType(); boolean staticCodeAnalysisEnabled = programmingExercise.isStaticCodeAnalysisEnabled(); boolean sequentialTestRunsEnabled = buildConfig.hasSequentialTestRuns(); - boolean testwiseCoverageEnabled = buildConfig.isTestwiseCoverageEnabled(); Windfile windfile; String dockerImage; try { windfile = buildConfig.getWindfile(); - dockerImage = windfile.getMetadata().docker().getFullImageName(); + dockerImage = windfile.metadata().docker().getFullImageName(); } catch (NullPointerException e) { log.warn("Could not retrieve windfile for programming exercise {}. Using default windfile instead.", programmingExercise.getId()); @@ -343,8 +336,8 @@ private BuildConfig getBuildConfig(ProgrammingExerciseParticipation participatio String buildScript = localCIBuildConfigurationService.createBuildScript(programmingExercise); return new BuildConfig(buildScript, dockerImage, commitHashToBuild, assignmentCommitHash, testCommitHash, branch, programmingLanguage, projectType, - staticCodeAnalysisEnabled, sequentialTestRunsEnabled, testwiseCoverageEnabled, resultPaths, buildConfig.getTimeoutSeconds(), - buildConfig.getAssignmentCheckoutPath(), buildConfig.getTestCheckoutPath(), buildConfig.getSolutionCheckoutPath(), dockerRunConfig); + staticCodeAnalysisEnabled, sequentialTestRunsEnabled, resultPaths, buildConfig.getTimeoutSeconds(), buildConfig.getAssignmentCheckoutPath(), + buildConfig.getTestCheckoutPath(), buildConfig.getSolutionCheckoutPath(), dockerRunConfig); } private ProgrammingExerciseBuildConfig loadBuildConfig(ProgrammingExercise programmingExercise) { diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseExportImportResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseExportImportResource.java index da74833808c0..7079d2e2994e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseExportImportResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseExportImportResource.java @@ -73,7 +73,7 @@ import de.tum.cit.aet.artemis.programming.domain.RepositoryType; import de.tum.cit.aet.artemis.programming.repository.AuxiliaryRepositoryRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTaskRepository; import de.tum.cit.aet.artemis.programming.service.ConsistencyCheckService; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseExportService; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseImportFromFileService; @@ -214,8 +214,7 @@ public ResponseEntity importProgrammingExercise(@PathVariab programmingExerciseRepository.validateCourseSettings(newExercise, course); final var originalProgrammingExercise = programmingExerciseRepository - .findByIdWithEagerBuildConfigTestCasesStaticCodeAnalysisCategoriesHintsAndTemplateAndSolutionParticipationsAndAuxReposAndSolutionEntriesAndBuildConfig( - sourceExerciseId) + .findByIdWithEagerBuildConfigTestCasesStaticCodeAnalysisCategoriesAndTemplateAndSolutionParticipationsAndAuxReposAndAndBuildConfig(sourceExerciseId) .orElseThrow(() -> new EntityNotFoundException("ProgrammingExercise", sourceExerciseId)); var consistencyErrors = consistencyCheckService.checkConsistencyOfProgrammingExercise(originalProgrammingExercise); @@ -261,7 +260,6 @@ public ResponseEntity importProgrammingExercise(@PathVariab importedProgrammingExercise.setStaticCodeAnalysisCategories(null); importedProgrammingExercise.setTemplateParticipation(null); importedProgrammingExercise.setSolutionParticipation(null); - importedProgrammingExercise.setExerciseHints(null); importedProgrammingExercise.setTasks(null); competencyProgressApi.updateProgressByLearningObjectAsync(importedProgrammingExercise); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/ProgrammingExerciseGitDiffReportResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseGitDiffReportResource.java similarity index 98% rename from src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/ProgrammingExerciseGitDiffReportResource.java rename to src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseGitDiffReportResource.java index 5dad0330b670..b561b6b331bb 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/ProgrammingExerciseGitDiffReportResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseGitDiffReportResource.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.web.hestia; +package de.tum.cit.aet.artemis.programming.web; import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; @@ -25,15 +25,15 @@ import de.tum.cit.aet.artemis.exercise.repository.ParticipationRepository; import de.tum.cit.aet.artemis.exercise.service.ParticipationAuthorizationCheckService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseGitDiffReport; import de.tum.cit.aet.artemis.programming.domain.RepositoryType; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; import de.tum.cit.aet.artemis.programming.dto.ProgrammingExerciseGitDiffReportDTO; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingSubmissionRepository; import de.tum.cit.aet.artemis.programming.service.CommitHistoryService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseGitDiffReportService; import de.tum.cit.aet.artemis.programming.service.RepositoryService; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseGitDiffReportService; /** * REST controller for managing ProgrammingExerciseGitDiffReports and its entries. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseResource.java index 0eaab82ce448..9d472c2cfbf1 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseResource.java @@ -86,10 +86,10 @@ import de.tum.cit.aet.artemis.programming.service.GitService; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseRepositoryService; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseTaskService; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseTestCaseService; import de.tum.cit.aet.artemis.programming.service.StaticCodeAnalysisService; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; import de.tum.cit.aet.artemis.programming.service.vcs.VersionControlService; import io.jsonwebtoken.lang.Arrays; @@ -305,10 +305,6 @@ public ResponseEntity updateProgrammingExercise(@RequestBod if (!Objects.equals(programmingExerciseBeforeUpdate.isStaticCodeAnalysisEnabled(), updatedProgrammingExercise.isStaticCodeAnalysisEnabled())) { throw new BadRequestAlertException("Static code analysis enabled flag must not be changed", ENTITY_NAME, "staticCodeAnalysisCannotChange"); } - if (!Objects.equals(programmingExerciseBeforeUpdate.getBuildConfig().isTestwiseCoverageEnabled(), - updatedProgrammingExercise.getBuildConfig().isTestwiseCoverageEnabled())) { - throw new BadRequestAlertException("Testwise coverage enabled flag must not be changed", ENTITY_NAME, "testwiseCoverageCannotChange"); - } // Check if theia Profile is enabled if (Arrays.asList(this.environment.getActiveProfiles()).contains(PROFILE_THEIA)) { // Require 1 / 3 participation modes to be enabled @@ -791,9 +787,7 @@ public ResponseEntity reEvaluateAndUpdateProgrammingExercis } /** - * DELETE programming-exercises/:exerciseId/tasks : Delete all tasks and solution entries for an existing ProgrammingExercise. - * Note: This endpoint exists only for testing purposes and will be removed at a later stage of the development of HESTIA - * (automatic generation of code hints for programming exercises in Java). + * DELETE programming-exercises/:exerciseId/tasks : Delete all tasks for an existing ProgrammingExercise. * * @param exerciseId of the exercise * @return the {@link ResponseEntity} with status {@code 204}, @@ -802,12 +796,12 @@ public ResponseEntity reEvaluateAndUpdateProgrammingExercis @DeleteMapping("programming-exercises/{exerciseId}/tasks") @EnforceAtLeastEditor @FeatureToggle(Feature.ProgrammingExercises) - public ResponseEntity deleteTaskWithSolutionEntries(@PathVariable Long exerciseId) { - log.debug("REST request to delete ProgrammingExerciseTasks with ProgrammingExerciseSolutionEntries for ProgrammingExercise with id : {}", exerciseId); + public ResponseEntity deleteTasks(@PathVariable Long exerciseId) { + log.debug("REST request to delete tasks for ProgrammingExercise with id : {}", exerciseId); ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.EDITOR, exercise, null); - programmingExerciseService.deleteTasksWithSolutionEntries(exercise.getId()); + programmingExerciseService.deleteTasks(exercise.getId()); return ResponseEntity.noContent().build(); } @@ -859,30 +853,6 @@ public ModelAndView redirectGetTemplateRepositoryFiles(@PathVariable Long exerci return new ModelAndView("forward:/api/repository/" + participation.getId() + "/files-content"); } - /** - * GET programming-exercises/:exerciseId/solution-file-names - *

- * Returns the solution repository file names for a given programming exercise. - * Note: This endpoint redirects the request to the ProgrammingExerciseParticipationService. This is required if - * the solution participation id is not known for the client. - * - * @param exerciseId the exercise for which the solution repository files should be retrieved - * @return a redirect to the endpoint returning the files with content - */ - @GetMapping("programming-exercises/{exerciseId}/file-names") - @EnforceAtLeastTutor - @FeatureToggle(Feature.ProgrammingExercises) - public ModelAndView redirectGetSolutionRepositoryFilesWithoutContent(@PathVariable Long exerciseId) { - log.debug("REST request to get latest solution repository file names for ProgrammingExercise with id : {}", exerciseId); - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.TEACHING_ASSISTANT, exercise, null); - - var participation = solutionProgrammingExerciseParticipationRepository.findByProgrammingExerciseIdElseThrow(exerciseId); - - // TODO: We want to get rid of ModelAndView and use ResponseEntity instead. Define an appropriate service method and then call it here and in the referenced endpoint. - return new ModelAndView("forward:/api/repository/" + participation.getId() + "/file-names"); - } - /** * GET programming-exercises/:exerciseId/build-log-statistics *

diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/ProgrammingExerciseTaskResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseTaskResource.java similarity index 94% rename from src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/ProgrammingExerciseTaskResource.java rename to src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseTaskResource.java index 379ebfacb035..64b0a34c2b16 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/ProgrammingExerciseTaskResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseTaskResource.java @@ -1,4 +1,4 @@ -package de.tum.cit.aet.artemis.programming.web.hestia; +package de.tum.cit.aet.artemis.programming.web; import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; @@ -18,9 +18,9 @@ import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastTutor; import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseTaskService; /** * REST controller for managing {@link ProgrammingExerciseTask}. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/CodeHintResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/CodeHintResource.java deleted file mode 100644 index 122aa11b7a17..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/CodeHintResource.java +++ /dev/null @@ -1,124 +0,0 @@ -package de.tum.cit.aet.artemis.programming.web.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.util.List; -import java.util.Objects; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import de.tum.cit.aet.artemis.core.exception.AccessForbiddenException; -import de.tum.cit.aet.artemis.core.exception.ConflictException; -import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInExercise.EnforceAtLeastEditorInExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CodeHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.CodeHintService; - -/** - * REST controller for managing {@link CodeHint}. - */ -@Profile(PROFILE_CORE) -@RestController -@RequestMapping("api/") -public class CodeHintResource { - - private static final Logger log = LoggerFactory.getLogger(CodeHintResource.class); - - private final ProgrammingExerciseRepository programmingExerciseRepository; - - private final ProgrammingExerciseSolutionEntryRepository solutionEntryRepository; - - private final CodeHintRepository codeHintRepository; - - private final CodeHintService codeHintService; - - public CodeHintResource(ProgrammingExerciseRepository programmingExerciseRepository, ProgrammingExerciseSolutionEntryRepository solutionEntryRepository, - CodeHintRepository codeHintRepository, CodeHintService codeHintService) { - this.programmingExerciseRepository = programmingExerciseRepository; - this.solutionEntryRepository = solutionEntryRepository; - this.codeHintRepository = codeHintRepository; - this.codeHintService = codeHintService; - } - - /** - * GET programming-exercises/{exerciseId}/code-hints: Retrieve all code hints for a programming exercise. - * - * @param exerciseId of the exercise - * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the code hints for the exercise - */ - @GetMapping("programming-exercises/{exerciseId}/code-hints") - @EnforceAtLeastEditorInExercise - public ResponseEntity> getAllCodeHints(@PathVariable Long exerciseId) { - var result = codeHintRepository.findByExerciseId(exerciseId); - return ResponseEntity.ok(result); - } - - /** - * {@code POST programming-exercises/:exerciseId/code-hints} : Create a new exerciseHint for an exercise. - * - * @param exerciseId the exerciseId of the exercise of which to create the exerciseHint - * @param deleteOldCodeHints Whether old code hints should be deleted - * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the new code hints - */ - @PostMapping("programming-exercises/{exerciseId}/code-hints") - @EnforceAtLeastEditorInExercise - public ResponseEntity> generateCodeHintsForExercise(@PathVariable Long exerciseId, - @RequestParam(value = "deleteOldCodeHints", defaultValue = "true") boolean deleteOldCodeHints) { - log.debug("REST request to generate CodeHints for ProgrammingExercise: {}", exerciseId); - - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - - // Hints for exam exercises are not supported at the moment - if (exercise.isExamExercise()) { - throw new AccessForbiddenException("Code hints for exams are currently not supported"); - } - - var codeHints = codeHintService.generateCodeHintsForExercise(exercise, deleteOldCodeHints); - return ResponseEntity.ok(codeHints); - } - - /** - * {@code DELETE programming-exercises/:exerciseId/code-hints/:codeHintId/solution-entries/:solutionEntryId} : - * Removes a solution entry from a code hint. - * - * @param exerciseId The id of the exercise of the code hint - * @param codeHintId The id of the code hint - * @param solutionEntryId The id of the solution entry - * @return 204 No Content - */ - @DeleteMapping("programming-exercises/{exerciseId}/code-hints/{codeHintId}/solution-entries/{solutionEntryId}") - @EnforceAtLeastEditorInExercise - public ResponseEntity removeSolutionEntryFromCodeHint(@PathVariable Long exerciseId, @PathVariable Long codeHintId, @PathVariable Long solutionEntryId) { - log.debug("REST request to remove SolutionEntry {} from CodeHint {} in ProgrammingExercise {}", solutionEntryId, codeHintId, exerciseId); - - var codeHint = codeHintRepository.findByIdWithSolutionEntriesElseThrow(codeHintId); - if (!Objects.equals(codeHint.getExercise().getId(), exerciseId)) { - throw new ConflictException("The code hint does not belong to the exercise", "CodeHint", "codeHintExerciseConflict"); - } - - var solutionEntry = codeHint.getSolutionEntries().stream().filter(solutionEntry1 -> solutionEntry1.getId().equals(solutionEntryId)).findFirst().orElse(null); - if (solutionEntry == null) { - throw new ConflictException("The solution entry does not belong to the code hint", "SolutionEntry", "solutionEntryCodeHintConflict"); - } - - solutionEntry.setCodeHint(null); - solutionEntryRepository.save(solutionEntry); - codeHint.getSolutionEntries().remove(solutionEntry); - - return ResponseEntity.noContent().build(); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/CoverageReportResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/CoverageReportResource.java deleted file mode 100644 index ad2c9a574268..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/CoverageReportResource.java +++ /dev/null @@ -1,68 +0,0 @@ -package de.tum.cit.aet.artemis.programming.web.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; -import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInExercise.EnforceAtLeastTutorInExercise; -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageReport; -import de.tum.cit.aet.artemis.programming.service.hestia.TestwiseCoverageService; - -/** - * REST controller for managing ProgrammingExerciseTestwiseCoverageReports and its entries. - */ -@Profile(PROFILE_CORE) -@RestController -@RequestMapping("api/") -public class CoverageReportResource { - - private static final Logger log = LoggerFactory.getLogger(CoverageReportResource.class); - - private final TestwiseCoverageService testwiseCoverageService; - - public CoverageReportResource(TestwiseCoverageService testwiseCoverageService) { - this.testwiseCoverageService = testwiseCoverageService; - } - - /** - * {@code GET exercises/:exerciseId/full-testwise-coverage-report} : Get the latest coverage report for a solution submission - * of a programming exercise with all file reports and descendants. - * - * @param exerciseId the exerciseId of the exercise of which to retrieve the testwise coverage report for the latest solution submission - * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the coverage report - */ - @GetMapping("programming-exercises/{exerciseId}/full-testwise-coverage-report") - @EnforceAtLeastTutorInExercise - public ResponseEntity getLatestFullCoverageReport(@PathVariable Long exerciseId) { - log.debug("REST request to get the latest Full Testwise CoverageReport for exercise {}", exerciseId); - - var optionalReportWithFileReports = testwiseCoverageService.getFullCoverageReportForLatestSolutionSubmissionFromProgrammingExercise(exerciseId) - .orElseThrow(() -> new EntityNotFoundException("Coverage report for exercise " + exerciseId + " not found.")); - return ResponseEntity.ok(optionalReportWithFileReports); - } - - /** - * {@code GET exercises/:exerciseId/testwise-coverage-report} : Get the latest coverage report for a solution submission - * of a programming exercise without the actual file reports. - * - * @param exerciseId the exerciseId of the exercise of which to retrieve the testwise coverage report for the latest solution submission - * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the coverage report - */ - @GetMapping("programming-exercises/{exerciseId}/testwise-coverage-report") - @EnforceAtLeastTutorInExercise - public ResponseEntity getLatestCoverageReport(@PathVariable Long exerciseId) { - log.debug("REST request to get the latest Testwise CoverageReport for exercise {}", exerciseId); - - var optionalReportWithoutFileReports = testwiseCoverageService.getCoverageReportForLatestSolutionSubmissionFromProgrammingExercise(exerciseId) - .orElseThrow(() -> new EntityNotFoundException("Coverage report for exercise " + exerciseId + " not found.")); - return ResponseEntity.ok(optionalReportWithoutFileReports); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/ExerciseHintResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/ExerciseHintResource.java deleted file mode 100644 index 029a374a2a87..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/ExerciseHintResource.java +++ /dev/null @@ -1,349 +0,0 @@ -package de.tum.cit.aet.artemis.programming.web.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Profile; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; -import de.tum.cit.aet.artemis.core.exception.ConflictException; -import de.tum.cit.aet.artemis.core.repository.UserRepository; -import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastStudent; -import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInExercise.EnforceAtLeastEditorInExercise; -import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInExercise.EnforceAtLeastStudentInExercise; -import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInExercise.EnforceAtLeastTutorInExercise; -import de.tum.cit.aet.artemis.exercise.domain.Exercise; -import de.tum.cit.aet.artemis.exercise.repository.ExerciseRepository; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.CodeHintService; -import de.tum.cit.aet.artemis.programming.service.hestia.ExerciseHintService; -import tech.jhipster.web.util.HeaderUtil; - -/** - * REST controller for managing {@link ExerciseHint}. - */ -@Profile(PROFILE_CORE) -@RestController -@RequestMapping("api/") -public class ExerciseHintResource { - - private static final String EXERCISE_HINT_ENTITY_NAME = "exerciseHint"; - - private static final String CODE_HINT_ENTITY_NAME = "codeHint"; - - private static final Logger log = LoggerFactory.getLogger(ExerciseHintResource.class); - - private final ExerciseHintService exerciseHintService; - - private final ExerciseHintRepository exerciseHintRepository; - - private final ProgrammingExerciseRepository programmingExerciseRepository; - - private final ExerciseRepository exerciseRepository; - - private final CodeHintService codeHintService; - - private final UserRepository userRepository; - - @Value("${jhipster.clientApp.name}") - private String applicationName; - - public ExerciseHintResource(ExerciseHintService exerciseHintService, ExerciseHintRepository exerciseHintRepository, ProgrammingExerciseRepository programmingExerciseRepository, - ExerciseRepository exerciseRepository, CodeHintService codeHintService, UserRepository userRepository) { - this.exerciseHintService = exerciseHintService; - this.exerciseHintRepository = exerciseHintRepository; - this.programmingExerciseRepository = programmingExerciseRepository; - this.exerciseRepository = exerciseRepository; - this.codeHintService = codeHintService; - this.userRepository = userRepository; - } - - /** - * {@code POST programming-exercises/:exerciseId/exercise-hints} : Create a new exerciseHint for an exercise. - * - * @param exerciseHint the exerciseHint to create - * @param exerciseId the exerciseId of the exercise of which to create the exerciseHint - * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new exerciseHint, - * or with status {@code 409 (Conflict)} if the exerciseId is invalid, - * @throws URISyntaxException if the Location URI syntax is incorrect. - */ - @PostMapping("programming-exercises/{exerciseId}/exercise-hints") - @EnforceAtLeastEditorInExercise - public ResponseEntity createExerciseHint(@RequestBody ExerciseHint exerciseHint, @PathVariable Long exerciseId) throws URISyntaxException { - log.debug("REST request to save ExerciseHint : {}", exerciseHint); - - // Reload the exercise from the database as we can't trust data from the client - Exercise exercise = exerciseRepository.findByIdElseThrow(exerciseId); - - if (exerciseHint instanceof CodeHint) { - throw new BadRequestAlertException("A code hint cannot be created manually.", CODE_HINT_ENTITY_NAME, "manualCodeHintOperation"); - } - if (exerciseHint.getExercise() == null) { - throw new ConflictException("An exercise hint can only be created if the exercise is defined.", EXERCISE_HINT_ENTITY_NAME, "exerciseNotDefined"); - } - - if (!exerciseHint.getExercise().getId().equals(exerciseId)) { - throw new ConflictException("An exercise hint can only be created if the exerciseIds match.", EXERCISE_HINT_ENTITY_NAME, "exerciseIdMismatch"); - } - - // Hints for exam exercises are not supported at the moment - if (exercise.isExamExercise()) { - throw new BadRequestAlertException("Exercise hints for exams are currently not supported", EXERCISE_HINT_ENTITY_NAME, "exerciseHintNotSupported"); - } - ExerciseHint result = exerciseHintRepository.save(exerciseHint); - return ResponseEntity.created(new URI("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints/" + result.getId())) - .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, EXERCISE_HINT_ENTITY_NAME, result.getId().toString())).body(result); - } - - /** - * {@code PUT programming-exercises/:exerciseId/exercise-hints/:exerciseHintId} : Updates an existing exerciseHint. - * - * @param exerciseHint the exerciseHint to update - * @param exerciseId the exerciseId of the exercise of which to update the exerciseHint - * @param exerciseHintId the id to the exerciseHint - * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated exerciseHint, - * or with status {@code 409 (Conflict} if the exerciseHint or exerciseId are not valid, - * or with status {@code 500 (Internal Server Error)} if the exerciseHint couldn't be updated. - */ - @PutMapping("programming-exercises/{exerciseId}/exercise-hints/{exerciseHintId}") - @EnforceAtLeastEditorInExercise - public ResponseEntity updateExerciseHint(@RequestBody ExerciseHint exerciseHint, @PathVariable Long exerciseHintId, @PathVariable Long exerciseId) { - log.debug("REST request to update ExerciseHint : {}", exerciseHint); - - // Reload the exercise from the database as we can't trust data from the client - Exercise exercise = exerciseRepository.findByIdElseThrow(exerciseId); - var hintBeforeSaving = exerciseHintRepository.findByIdWithRelationsElseThrow(exerciseHintId); - - if (!exerciseHint.getClass().equals(hintBeforeSaving.getClass())) { - throw new BadRequestAlertException("A code hint cannot be converted to or from a normal hint.", CODE_HINT_ENTITY_NAME, "manualCodeHintOperation"); - } - - if (exerciseHint.getId() == null || !exerciseHintId.equals(exerciseHint.getId()) || exerciseHint.getExercise() == null) { - throw new ConflictException("An exercise hint can only be changed if it has an ID and if the exercise is not null.", EXERCISE_HINT_ENTITY_NAME, "exerciseNotDefined"); - } - - if (!exerciseHint.getExercise().getId().equals(exerciseId)) { - throw new ConflictException("An exercise hint can only be updated if the exerciseIds match.", EXERCISE_HINT_ENTITY_NAME, "exerciseIdsMismatch"); - } - - // Hints for exam exercises are not supported at the moment - if (exercise.isExamExercise()) { - throw new BadRequestAlertException("Exercise hints for exams are currently not supported", EXERCISE_HINT_ENTITY_NAME, "exerciseHintNotSupported"); - } - - if (exerciseHint instanceof CodeHint codeHint && codeHint.getSolutionEntries() != null) { - codeHintService.updateSolutionEntriesForCodeHint(codeHint); - } - exerciseHint.setExerciseHintActivations(hintBeforeSaving.getExerciseHintActivations()); - ExerciseHint result = exerciseHintRepository.save(exerciseHint); - return ResponseEntity.ok().headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, EXERCISE_HINT_ENTITY_NAME, exerciseHint.getId().toString())).body(result); - } - - /** - * {@code GET programming-exercises/:exerciseId/exercise-hints/:exerciseHintId/title} : Returns the title of the hint with the given id - * - * @param exerciseHintId the id of the exerciseHint - * @param exerciseId the exerciseId of the exercise of which to retrieve the exerciseHints' title - * @return the title of the hint wrapped in an ResponseEntity or 404 Not Found if no hint with that id exists - * or with status {@code 409 (Conflict)} if the exerciseId is not valid. - */ - @GetMapping("programming-exercises/{exerciseId}/exercise-hints/{exerciseHintId}/title") - @EnforceAtLeastStudent - public ResponseEntity getHintTitle(@PathVariable Long exerciseId, @PathVariable Long exerciseHintId) { - var title = exerciseHintService.getExerciseHintTitle(exerciseId, exerciseHintId); - return title == null ? ResponseEntity.notFound().build() : ResponseEntity.ok(title); - } - - /** - * {@code GET programming-exercises/:exerciseId/exercise-hints/:exerciseHintId} : get the exerciseHint with the given id. - * - * @param exerciseHintId the id of the exerciseHint to retrieve. - * @param exerciseId the exerciseId of the exercise of which to retrieve the exerciseHint - * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the exerciseHint, - * or with status {@code 404 (Not Found)}, - * or with status {@code 409 (Conflict)} if the exerciseId is not valid. - */ - @GetMapping("programming-exercises/{exerciseId}/exercise-hints/{exerciseHintId}") - @EnforceAtLeastTutorInExercise - public ResponseEntity getExerciseHint(@PathVariable Long exerciseId, @PathVariable Long exerciseHintId) { - log.debug("REST request to get ExerciseHint : {}", exerciseHintId); - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - if (exercise.isExamExercise()) { - // not allowed for exam exercises - throw new BadRequestAlertException("Exercise hints for exams are currently not supported", EXERCISE_HINT_ENTITY_NAME, "exerciseHintNotSupported"); - } - - var exerciseHint = exerciseHintRepository.findByIdWithRelationsElseThrow(exerciseHintId); - - if (!exerciseHint.getExercise().getId().equals(exerciseId)) { - throw new ConflictException("An exercise hint can only be retrieved if the exerciseIds match.", EXERCISE_HINT_ENTITY_NAME, "exerciseIdsMismatch"); - } - - return ResponseEntity.ok().body(exerciseHint); - } - - /** - * {@code GET programming-exercises/:exerciseId/exercise-hints} : get the exerciseHints of a provided exercise. - * - * @param exerciseId the exercise id of which to retrieve the exercise hints. - * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the exerciseHint, - * or with status {@code 404 (Not Found)}, - * or with status {@code 409 (Conflict)} if the exerciseId is not valid. - */ - @GetMapping("programming-exercises/{exerciseId}/exercise-hints") - @EnforceAtLeastTutorInExercise - public ResponseEntity> getExerciseHintsForExercise(@PathVariable Long exerciseId) { - log.debug("REST request to get ExerciseHints : {}", exerciseId); - var exerciseHints = exerciseHintRepository.findByExerciseIdWithRelations(exerciseId); - return ResponseEntity.ok(exerciseHints); - } - - /** - * {@code GET programming-exercises/:exerciseId/exercise-hints/activated} : get the exercise hints of a provided exercise that the user has activated - * - * @param exerciseId the exercise id of which to retrieve the exercise hints. - * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the exercise hints, - * or with status {@code 404 (Not Found)} - */ - @GetMapping("programming-exercises/{exerciseId}/exercise-hints/activated") - @EnforceAtLeastStudentInExercise - public ResponseEntity> getActivatedExerciseHintsForExercise(@PathVariable Long exerciseId) { - log.debug("REST request to get activated ExerciseHints : {}", exerciseId); - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - if (exercise.isExamExercise()) { - // not allowed for exam exercises - throw new BadRequestAlertException("Exercise hints for exams are currently not supported", EXERCISE_HINT_ENTITY_NAME, "exerciseHintNotSupported"); - } - - var user = userRepository.getUserWithGroupsAndAuthorities(); - var exerciseHints = exerciseHintService.getActivatedExerciseHints(exercise, user); - return ResponseEntity.ok(exerciseHints); - } - - /** - * {@code GET programming-exercises/:exerciseId/available-exercise-hints} : get the available exercise hints. - * - * @param exerciseId the exerciseId of the exercise of which to retrieve the exercise hint - * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the exercise hints - */ - @GetMapping("programming-exercises/{exerciseId}/exercise-hints/available") - @EnforceAtLeastStudentInExercise - public ResponseEntity> getAvailableExerciseHintsForExercise(@PathVariable Long exerciseId) { - log.debug("REST request to get a CodeHint for programming exercise : {}", exerciseId); - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - if (exercise.isExamExercise()) { - // not allowed for exam exercises - throw new BadRequestAlertException("Exercise hints for exams are currently not supported", EXERCISE_HINT_ENTITY_NAME, "exerciseHintNotSupported"); - } - - var user = userRepository.getUserWithGroupsAndAuthorities(); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, user); - availableExerciseHints.forEach(ExerciseHint::removeContent); - - return ResponseEntity.ok().body(availableExerciseHints); - } - - /** - * {@code POST programming-exercises/:exerciseId/exercise-hints/:exerciseHintId/activate} - * Activates a single exercise hint of an exercise for the logged-in user - * - * @param exerciseId The id of the exercise of which to activate the exercise hint - * @param exerciseHintId The id of the exercise hint to activate - * @return The {@link ResponseEntity} with status {@code 200 (OK)} and with body the activated exercise hint with content - * or with status {@code 400 (BAD_REQUEST)} if the hint could not be activated - */ - @PostMapping("programming-exercises/{exerciseId}/exercise-hints/{exerciseHintId}/activate") - @EnforceAtLeastStudentInExercise - public ResponseEntity activateExerciseHint(@PathVariable Long exerciseId, @PathVariable Long exerciseHintId) { - log.debug("REST request to activate ExerciseHint : {}", exerciseHintId); - var exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - var user = userRepository.getUserWithGroupsAndAuthorities(); - - var exerciseHint = exerciseHintRepository.findByIdWithRelationsElseThrow(exerciseHintId); - - if (!exerciseHint.getExercise().getId().equals(exercise.getId())) { - throw new ConflictException("An exercise hint can only be deleted if the exerciseIds match.", EXERCISE_HINT_ENTITY_NAME, "exerciseIdsMismatch"); - } - - if (exerciseHintService.activateHint(exerciseHint, user)) { - return ResponseEntity.ok(exerciseHint); - } - else { - throw new BadRequestAlertException("Unable to activate exercise hint", EXERCISE_HINT_ENTITY_NAME, "exerciseHintIdActivationFailed"); - } - } - - /** - * {@code POST programming-exercises/:exerciseId/exercise-hints/:exerciseHintId/rating/:ratingValue}: Rates an exercise hint - * - * @param exerciseId The id of the exercise of which to activate the exercise hint - * @param exerciseHintId The id of the exercise hint to activate - * @param ratingValue The value of the rating - * @return The {@link ResponseEntity} with status {@code 200 (OK)} - */ - @PostMapping("programming-exercises/{exerciseId}/exercise-hints/{exerciseHintId}/rating/{ratingValue}") - @EnforceAtLeastStudentInExercise - public ResponseEntity rateExerciseHint(@PathVariable Long exerciseId, @PathVariable Long exerciseHintId, @PathVariable Integer ratingValue) { - log.debug("REST request to rate ExerciseHint : {}", exerciseHintId); - var user = userRepository.getUserWithGroupsAndAuthorities(); - - var exerciseHint = exerciseHintRepository.findByIdWithRelationsElseThrow(exerciseHintId); - if (!exerciseHint.getExercise().getId().equals(exerciseId)) { - throw new ConflictException("An exercise hint can only be deleted if the exerciseIds match.", EXERCISE_HINT_ENTITY_NAME, "exerciseIdsMismatch"); - } - - exerciseHintService.rateExerciseHint(exerciseHint, user, ratingValue); - - return ResponseEntity.ok().build(); - } - - /** - * {@code DELETE programming-exercises/:exerciseId/exercise-hints/:exerciseHintId} : delete the exerciseHint with given id. - * - * @param exerciseHintId the id of the exerciseHint to delete - * @param exerciseId the exercise id of which to delete the exercise hint - * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}, - * or with status {@code 409 (Conflict)} if the exerciseId is not valid. - */ - @DeleteMapping("programming-exercises/{exerciseId}/exercise-hints/{exerciseHintId}") - @EnforceAtLeastEditorInExercise - public ResponseEntity deleteExerciseHint(@PathVariable Long exerciseId, @PathVariable Long exerciseHintId) { - log.debug("REST request to delete ExerciseHint : {}", exerciseHintId); - var exerciseHint = exerciseHintRepository.findByIdElseThrow(exerciseHintId); - - if (!exerciseHint.getExercise().getId().equals(exerciseId)) { - throw new ConflictException("An exercise hint can only be deleted if the exerciseIds match.", EXERCISE_HINT_ENTITY_NAME, "exerciseIdsMismatch"); - } - - String entityName; - - if (exerciseHint instanceof CodeHint codeHint) { - codeHintService.deleteCodeHint(codeHint); - entityName = CODE_HINT_ENTITY_NAME; - } - else { - exerciseHintRepository.deleteById(exerciseHintId); - entityName = EXERCISE_HINT_ENTITY_NAME; - } - return ResponseEntity.noContent().headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, entityName, exerciseHintId.toString())).build(); - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/ProgrammingExerciseSolutionEntryResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/ProgrammingExerciseSolutionEntryResource.java deleted file mode 100644 index c18e84806672..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/ProgrammingExerciseSolutionEntryResource.java +++ /dev/null @@ -1,337 +0,0 @@ -package de.tum.cit.aet.artemis.programming.web.hestia; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Profile; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import de.tum.cit.aet.artemis.core.exception.ConflictException; -import de.tum.cit.aet.artemis.core.exception.InternalServerErrorException; -import de.tum.cit.aet.artemis.core.security.Role; -import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastEditor; -import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastStudent; -import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastTutor; -import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CodeHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralSolutionEntryGenerationException; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralTestCaseService; -import de.tum.cit.aet.artemis.programming.service.hestia.structural.StructuralSolutionEntryGenerationException; -import de.tum.cit.aet.artemis.programming.service.hestia.structural.StructuralTestCaseService; -import tech.jhipster.web.util.HeaderUtil; - -/** - * REST controller for managing {@link ProgrammingExerciseSolutionEntry}. - */ -@Profile(PROFILE_CORE) -@RestController -@RequestMapping("api/") -public class ProgrammingExerciseSolutionEntryResource { - - private static final Logger log = LoggerFactory.getLogger(ProgrammingExerciseSolutionEntryResource.class); - - private static final String ENTITY_NAME = "programmingExerciseSolutionEntry"; - - @Value("${jhipster.clientApp.name}") - private String applicationName; - - private final ProgrammingExerciseSolutionEntryRepository programmingExerciseSolutionEntryRepository; - - private final ProgrammingExerciseRepository programmingExerciseRepository; - - private final CodeHintRepository codeHintRepository; - - private final ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository; - - private final AuthorizationCheckService authCheckService; - - private final StructuralTestCaseService structuralTestCaseService; - - private final BehavioralTestCaseService behavioralTestCaseService; - - public ProgrammingExerciseSolutionEntryResource(ProgrammingExerciseSolutionEntryRepository programmingExerciseSolutionEntryRepository, - ProgrammingExerciseRepository programmingExerciseRepository, CodeHintRepository codeHintRepository, - ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository, AuthorizationCheckService authCheckService, - StructuralTestCaseService structuralTestCaseService, BehavioralTestCaseService behavioralTestCaseService) { - this.programmingExerciseSolutionEntryRepository = programmingExerciseSolutionEntryRepository; - this.programmingExerciseRepository = programmingExerciseRepository; - this.codeHintRepository = codeHintRepository; - this.programmingExerciseTestCaseRepository = programmingExerciseTestCaseRepository; - this.authCheckService = authCheckService; - this.structuralTestCaseService = structuralTestCaseService; - this.behavioralTestCaseService = behavioralTestCaseService; - } - - /** - * GET programming-exercises/:exerciseId/solution-entries/:solutionEntryId : Get the solution entry with test cases and programming exercise - * - * @param exerciseId of the exercise - * @param solutionEntryId of the solution entry - * @return the {@link ResponseEntity} with status {@code 200} and with body the solution entries with test cases and exercise, - * or with status {@code 409 (Conflict)} if the exerciseId or solutionEntryId are not valid. - */ - @GetMapping("programming-exercises/{exerciseId}/solution-entries/{solutionEntryId}") - @EnforceAtLeastTutor - public ResponseEntity getSolutionEntry(@PathVariable Long exerciseId, @PathVariable Long solutionEntryId) { - log.debug("REST request to retrieve SolutionEntry : {}", solutionEntryId); - // Reload the exercise from the database as we can't trust data from the client - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.TEACHING_ASSISTANT, exercise, null); - - ProgrammingExerciseSolutionEntry solutionEntry = programmingExerciseSolutionEntryRepository.findByIdWithTestCaseAndProgrammingExerciseElseThrow(solutionEntryId); - - if (!exerciseId.equals(solutionEntry.getTestCase().getExercise().getId())) { - throw new ConflictException("A solution entry can only be retrieved if the exercise match", ENTITY_NAME, "exerciseIdsMismatch"); - } - return ResponseEntity.ok(solutionEntry); - } - - /** - * GET programming-exercises/{exerciseId}/solution-entries: Get all solution entries with test cases for a programming exercise - * - * @param exerciseId of the exercise - * @return the {@link ResponseEntity} with status {@code 200} and with body the solution entries with test cases. - */ - @GetMapping("programming-exercises/{exerciseId}/solution-entries") - @EnforceAtLeastEditor - public ResponseEntity> getAllSolutionEntries(@PathVariable Long exerciseId) { - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.EDITOR, exercise, null); - - var result = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); - return ResponseEntity.ok(result); - } - - /** - * GET programming-exercises/:exerciseId/code-hints/:codeHintId/solution-entries : Get all solution entries for a given code hint - * - * @param exerciseId of the exercise - * @param codeHintId of the code hint - * @return the {@link ResponseEntity} with status {@code 200} and with body the solution entries, - * or with status {@code 409 (Conflict)} if the exerciseId or codeHintId are not valid. - */ - @GetMapping("programming-exercises/{exerciseId}/code-hints/{codeHintId}/solution-entries") - @EnforceAtLeastStudent - public ResponseEntity> getSolutionEntriesForCodeHint(@PathVariable Long exerciseId, @PathVariable Long codeHintId) { - log.debug("REST request to retrieve SolutionEntry for CodeHint with id : {}", codeHintId); - // Reload the exercise from the database as we can't trust data from the client - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.STUDENT, exercise, null); - - CodeHint codeHint = codeHintRepository.findByIdElseThrow(codeHintId); - if (!exercise.getId().equals(codeHint.getExercise().getId())) { - throw new ConflictException("A solution entry can only be retrieved if the code hint belongs to the exercise", ENTITY_NAME, "exerciseIdsMismatch"); - } - - Set solutionEntries = programmingExerciseSolutionEntryRepository.findByCodeHintId(codeHintId); - return ResponseEntity.ok(solutionEntries); - } - - /** - * GET programming-exercises/:exerciseId/test-cases/:testCaseId/solution-entries : Get all solution entries for a given test case - * - * @param exerciseId of the exercise - * @param testCaseId of the test case - * @return the {@link ResponseEntity} with status {@code 200} and with body the solution entries, - * or with status {@code 409 (Conflict)} if the exerciseId or testCaseId are not valid. - */ - @GetMapping("programming-exercises/{exerciseId}/test-cases/{testCaseId}/solution-entries") - @EnforceAtLeastStudent - public ResponseEntity> getSolutionEntriesForTestCase(@PathVariable Long exerciseId, @PathVariable Long testCaseId) { - log.debug("REST request to retrieve SolutionEntry for ProgrammingExerciseTestCase with id : {}", testCaseId); - // Reload the exercise from the database as we can't trust data from the client - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.STUDENT, exercise, null); - - ProgrammingExerciseTestCase testCase = programmingExerciseTestCaseRepository.findByIdWithExerciseElseThrow(testCaseId); - if (!exercise.getId().equals(testCase.getExercise().getId())) { - throw new ConflictException("A solution entry can only be retrieved if the test case belongs to the exercise", ENTITY_NAME, "exerciseIdsMismatch"); - } - - Set solutionEntries = programmingExerciseSolutionEntryRepository.findByTestCaseId(testCaseId); - return ResponseEntity.ok(solutionEntries); - } - - /** - * POST programming-exercises/:exerciseId/test-cases/:testCaseId/solution-entries : Create a solution entry for a test case - * - * @param exerciseId of the exercise - * @param testCaseId of the test case - * @param programmingExerciseSolutionEntry the solution entry to be created - * @return the {@link ResponseEntity} with status {@code 201} and with body the created solution entry, - * or with status {@code 409 (Conflict)} if the exerciseId, testcaseId, or solution entry are not valid. - */ - @PostMapping("programming-exercises/{exerciseId}/test-cases/{testCaseId}/solution-entries") - @EnforceAtLeastEditor - public ResponseEntity createSolutionEntryForTestCase(@PathVariable Long exerciseId, @PathVariable Long testCaseId, - @RequestBody ProgrammingExerciseSolutionEntry programmingExerciseSolutionEntry) throws URISyntaxException { - log.debug("REST request to create SolutionEntry : {}", programmingExerciseSolutionEntry); - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.EDITOR, exercise, null); - - ProgrammingExerciseTestCase testCase = programmingExerciseTestCaseRepository.findByIdWithExerciseElseThrow(testCaseId); - checkExerciseContainsTestCaseElseThrow(exercise, testCase); - - ProgrammingExerciseSolutionEntry result = programmingExerciseSolutionEntryRepository.save(programmingExerciseSolutionEntry); - return ResponseEntity.created(new URI("/api/programming-exercises/" + exerciseId + "/solution-entries/" + result.getId())) - .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())).body(result); - } - - /** - * PUT programming-exercises/:exerciseId/test-cases/:testCaseId/solution-entries/:solutionEntryId : Update a solution entry - * - * @param exerciseId of the exercise - * @param testCaseId of the test case - * @param solutionEntryId of the solution entry - * @param solutionEntry the updated solution entry - * @return the {@link ResponseEntity} with status {@code 200} and with body the updated solution entry, - * or with status {@code 409 (Conflict)} if the exerciseId, testcaseId, solutionEntryId, or solution entry are not valid. - */ - @PutMapping("programming-exercises/{exerciseId}/test-cases/{testCaseId}/solution-entries/{solutionEntryId}") - @EnforceAtLeastEditor - public ResponseEntity updateSolutionEntry(@PathVariable Long exerciseId, @PathVariable Long testCaseId, @PathVariable Long solutionEntryId, - @RequestBody ProgrammingExerciseSolutionEntry solutionEntry) { - log.debug("REST request to update SolutionEntry : {}", solutionEntry); - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.EDITOR, exercise, null); - - ProgrammingExerciseTestCase testCase = programmingExerciseTestCaseRepository.findByIdWithExerciseElseThrow(testCaseId); - ProgrammingExerciseSolutionEntry solutionEntryBeforeSaving = programmingExerciseSolutionEntryRepository - .findByIdWithTestCaseAndProgrammingExerciseElseThrow(solutionEntryId); - - checkExerciseContainsTestCaseElseThrow(exercise, testCase); - if (!solutionEntryId.equals(solutionEntryBeforeSaving.getId())) { - throw new ConflictException("A solution entry can only be updated if the solutionEntryIds match", ENTITY_NAME, "solutionEntryError"); - } - - ProgrammingExerciseSolutionEntry solutionEntryAfterSaving = programmingExerciseSolutionEntryRepository.save(solutionEntry); - return ResponseEntity.ok().headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, solutionEntryAfterSaving.getId().toString())) - .body(solutionEntryAfterSaving); - } - - /** - * DELETE programming-exercises/:exerciseId/test-cases/:testCaseId/solution-entries/:solutionEntryId : Delete a solution entry - * - * @param exerciseId of the exercise - * @param testCaseId of the test case - * @param solutionEntryId of the solution entry that is to be deleted - * @return the {@link ResponseEntity} with status {@code 204}, - * or with status {@code 409 (Conflict)} if the exerciseId, testcaseId, or solutionEntryId are not valid. - */ - @DeleteMapping("programming-exercises/{exerciseId}/test-cases/{testCaseId}/solution-entries/{solutionEntryId}") - @EnforceAtLeastEditor - public ResponseEntity deleteSolutionEntry(@PathVariable Long exerciseId, @PathVariable Long testCaseId, @PathVariable Long solutionEntryId) { - log.debug("REST request to delete SolutionEntry with id : {}", solutionEntryId); - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.EDITOR, exercise, null); - - ProgrammingExerciseTestCase testCase = programmingExerciseTestCaseRepository.findByIdWithExerciseElseThrow(testCaseId); - ProgrammingExerciseSolutionEntry solutionEntry = programmingExerciseSolutionEntryRepository.findByIdWithTestCaseAndProgrammingExerciseElseThrow(solutionEntryId); - - checkExerciseContainsTestCaseElseThrow(exercise, testCase); - checkTestCaseContainsSolutionEntryElseThrow(testCase, solutionEntry); - - programmingExerciseSolutionEntryRepository.deleteById(solutionEntryId); - return ResponseEntity.noContent().headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, solutionEntry.getId().toString())).build(); - } - - /** - * DELETE programming-exercises/:exerciseId/solution-entries: Delete all solution entries for a programming exercise - * - * @param exerciseId of the exercise - * @return the {@link ResponseEntity} with status {@code 204}, - * or with status {@code 404} if the exerciseId is not valid. - */ - @DeleteMapping("programming-exercises/{exerciseId}/solution-entries") - @EnforceAtLeastEditor - public ResponseEntity deleteAllSolutionEntriesForExercise(@PathVariable Long exerciseId) { - log.debug("REST request to delete all SolutionEntries for exercise with id: {}", exerciseId); - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.EDITOR, exercise, null); - - var entriesToDelete = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); - - programmingExerciseSolutionEntryRepository.deleteAll(entriesToDelete); - return ResponseEntity.noContent().build(); - } - - /** - * POST programming-exercises/:exerciseId/structural-solution-entries : Create the structural solution entries for a programming exercise - * - * @param exerciseId of the exercise - * @return the {@link ResponseEntity} with status {@code 200} and with body the created solution entries, - */ - @PostMapping("programming-exercises/{exerciseId}/structural-solution-entries") - @EnforceAtLeastEditor - public ResponseEntity> createStructuralSolutionEntries(@PathVariable Long exerciseId) { - log.debug("REST request to create structural solution entries"); - ProgrammingExercise exercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(exerciseId); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.EDITOR, exercise, null); - - try { - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - return ResponseEntity.ok(solutionEntries); - } - catch (StructuralSolutionEntryGenerationException e) { - log.error("Unable to create structural solution entries", e); - throw new InternalServerErrorException(e.getMessage()); - } - } - - /** - * POST programming-exercises/:exerciseId/behavioral-solution-entries : Create the behavioral solution entries for a programming exercise - * - * @param exerciseId of the exercise - * @return the {@link ResponseEntity} with status {@code 200} and with body the created solution entries, - */ - @PostMapping("programming-exercises/{exerciseId}/behavioral-solution-entries") - @EnforceAtLeastEditor - public ResponseEntity> createBehavioralSolutionEntries(@PathVariable Long exerciseId) { - log.debug("REST request to create behavioral solution entries"); - ProgrammingExercise exercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationTeamAssignmentConfigCategoriesAndBuildConfigElseThrow(exerciseId); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.EDITOR, exercise, null); - - try { - var solutionEntries = behavioralTestCaseService.generateBehavioralSolutionEntries(exercise); - return ResponseEntity.ok(solutionEntries); - } - catch (BehavioralSolutionEntryGenerationException e) { - log.error("Unable to create behavioral solution entries", e); - throw new InternalServerErrorException(e.getMessage()); - } - } - - private void checkExerciseContainsTestCaseElseThrow(ProgrammingExercise exercise, ProgrammingExerciseTestCase testCase) { - if (!exercise.getId().equals(testCase.getExercise().getId())) { - throw new ConflictException("The test case of the solution entry does not belong to the exercise.", ENTITY_NAME, "exerciseIdsMismatch"); - } - } - - private void checkTestCaseContainsSolutionEntryElseThrow(ProgrammingExerciseTestCase testCase, ProgrammingExerciseSolutionEntry solutionEntry) { - if (solutionEntry.getTestCase() == null || !testCase.getId().equals(solutionEntry.getTestCase().getId())) { - throw new ConflictException("The test case of the solution entry does not belong to the solution entry.", ENTITY_NAME, "solutionEntryError"); - } - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/localci/AeolusTemplateResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/localci/AeolusTemplateResource.java index 9e5530e94813..a20ce1b7c163 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/localci/AeolusTemplateResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/localci/AeolusTemplateResource.java @@ -21,13 +21,13 @@ import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastEditor; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProjectType; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; import de.tum.cit.aet.artemis.programming.service.BuildScriptProviderService; import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusTemplateService; -import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile; /** * Service for retrieving aeolus template files based on the programming language, project type, and - * the different options (static analysis, sequential runs, test coverage) as well as the default + * the different options (static analysis, sequential runs) as well as the default * image for the programming language and project type for the artemis instance. */ @Profile("aeolus | localci") @@ -61,21 +61,19 @@ public AeolusTemplateResource(AeolusTemplateService aeolusTemplateService, Build * @param projectType The project type for which the template file should be returned. If omitted, a default depending on the language will be used. * @param staticAnalysis Whether the static analysis template should be used * @param sequentialRuns Whether the sequential runs template should be used - * @param testCoverage Whether the test coverage template should be used * @return The requested file, or 404 if the file doesn't exist */ @GetMapping({ "templates/{language}/{projectType}", "templates/{language}" }) @EnforceAtLeastEditor public ResponseEntity getAeolusTemplate(@PathVariable ProgrammingLanguage language, @PathVariable Optional projectType, @RequestParam(value = "staticAnalysis", defaultValue = "false") boolean staticAnalysis, - @RequestParam(value = "sequentialRuns", defaultValue = "false") boolean sequentialRuns, - @RequestParam(value = "testCoverage", defaultValue = "false") boolean testCoverage) { - log.debug("REST request to get aeolus template for programming language {} and project type {}, static Analysis: {}, sequential Runs {}, testCoverage: {}", language, - projectType, staticAnalysis, sequentialRuns, testCoverage); + @RequestParam(value = "sequentialRuns", defaultValue = "false") boolean sequentialRuns) { + log.debug("REST request to get aeolus template for programming language {} and project type {}, static Analysis: {}, sequential Runs {}", language, projectType, + staticAnalysis, sequentialRuns); String projectTypePrefix = projectType.map(type -> type.name().toLowerCase()).orElse(""); - return getAeolusTemplateFileContentWithResponse(language, projectTypePrefix, staticAnalysis, sequentialRuns, testCoverage); + return getAeolusTemplateFileContentWithResponse(language, projectTypePrefix, staticAnalysis, sequentialRuns); } /** @@ -88,21 +86,19 @@ public ResponseEntity getAeolusTemplate(@PathVariable ProgrammingLanguag * @param projectType The project type for which the template file should be returned. If omitted, a default depending on the language will be used. * @param staticAnalysis Whether the static analysis template should be used * @param sequentialRuns Whether the sequential runs template should be used - * @param testCoverage Whether the test coverage template should be used * @return The requested file, or 404 if the file doesn't exist */ @GetMapping({ "template-scripts/{language}/{projectType}", "template-scripts/{language}" }) @EnforceAtLeastEditor public ResponseEntity getAeolusTemplateScript(@PathVariable ProgrammingLanguage language, @PathVariable Optional projectType, @RequestParam(value = "staticAnalysis", defaultValue = "false") boolean staticAnalysis, - @RequestParam(value = "sequentialRuns", defaultValue = "false") boolean sequentialRuns, - @RequestParam(value = "testCoverage", defaultValue = "false") boolean testCoverage) { - log.debug("REST request to get aeolus template for programming language {} and project type {}, static Analysis: {}, sequential Runs {}, testCoverage: {}", language, - projectType, staticAnalysis, sequentialRuns, testCoverage); + @RequestParam(value = "sequentialRuns", defaultValue = "false") boolean sequentialRuns) { + log.debug("REST request to get aeolus template script for programming language {} and project type {}, static Analysis: {}, sequential Runs {}", language, projectType, + staticAnalysis, sequentialRuns); String projectTypePrefix = projectType.map(type -> type.name().toLowerCase()).orElse(""); - return getAeolusTemplateScriptWithResponse(language, projectTypePrefix, staticAnalysis, sequentialRuns, testCoverage); + return getAeolusTemplateScriptWithResponse(language, projectTypePrefix, staticAnalysis, sequentialRuns); } /** @@ -115,17 +111,16 @@ public ResponseEntity getAeolusTemplateScript(@PathVariable ProgrammingL * @param projectTypePrefix The project type for which the template file should be returned. If omitted, a default depending on the language will be used. * @param staticAnalysis Whether the static analysis template should be used * @param sequentialRuns Whether the sequential runs template should be used - * @param testCoverage Whether the test coverage template should be used * @return The requested file, or 404 if the file doesn't exist */ - private ResponseEntity getAeolusTemplateFileContentWithResponse(ProgrammingLanguage language, String projectTypePrefix, boolean staticAnalysis, boolean sequentialRuns, - boolean testCoverage) { + private ResponseEntity getAeolusTemplateFileContentWithResponse(ProgrammingLanguage language, String projectTypePrefix, boolean staticAnalysis, + boolean sequentialRuns) { try { Optional optionalProjectType = Optional.empty(); if (!projectTypePrefix.isEmpty()) { optionalProjectType = Optional.of(ProjectType.valueOf(projectTypePrefix.toUpperCase())); } - Windfile windfile = aeolusTemplateService.getWindfileFor(language, optionalProjectType, staticAnalysis, sequentialRuns, testCoverage); + Windfile windfile = aeolusTemplateService.getWindfileFor(language, optionalProjectType, staticAnalysis, sequentialRuns); if (windfile == null) { return new ResponseEntity<>(null, null, HttpStatus.NOT_FOUND); } @@ -148,17 +143,15 @@ private ResponseEntity getAeolusTemplateFileContentWithResponse(Programm * @param projectTypePrefix The project type for which the template file should be returned. If omitted, a default depending on the language will be used. * @param staticAnalysis Whether the static analysis template should be used * @param sequentialRuns Whether the sequential runs template should be used - * @param testCoverage Whether the test coverage template should be used * @return The requested file, or 404 if the file doesn't exist */ - private ResponseEntity getAeolusTemplateScriptWithResponse(ProgrammingLanguage language, String projectTypePrefix, boolean staticAnalysis, boolean sequentialRuns, - boolean testCoverage) { + private ResponseEntity getAeolusTemplateScriptWithResponse(ProgrammingLanguage language, String projectTypePrefix, boolean staticAnalysis, boolean sequentialRuns) { try { Optional optionalProjectType = Optional.empty(); if (!projectTypePrefix.isEmpty()) { optionalProjectType = Optional.of(ProjectType.valueOf(projectTypePrefix.toUpperCase())); } - String script = buildScriptProviderService.getScriptFor(language, optionalProjectType, staticAnalysis, sequentialRuns, testCoverage); + String script = buildScriptProviderService.getScriptFor(language, optionalProjectType, staticAnalysis, sequentialRuns); HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.setContentType(MediaType.TEXT_PLAIN); return new ResponseEntity<>(script, responseHeaders, HttpStatus.OK); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryProgrammingExerciseParticipationResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryProgrammingExerciseParticipationResource.java index 6cb9df0b10ee..28515ba970bb 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryProgrammingExerciseParticipationResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryProgrammingExerciseParticipationResource.java @@ -7,8 +7,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; import jakarta.servlet.http.HttpServletRequest; @@ -294,24 +292,6 @@ public ResponseEntity> getFilesWithContent(@PathVariable Lon }); } - /** - * GET /repository/{participationId}/file-names: Gets the file names of the repository - * - * @param participationId participation of the student/template/solution - * @return the ResponseEntity with status 200 (OK) and a set of file names - */ - @GetMapping(value = "repository/{participationId}/file-names", produces = MediaType.APPLICATION_JSON_VALUE) - @EnforceAtLeastTutor - public ResponseEntity> getFileNames(@PathVariable Long participationId) { - return super.executeAndCheckForExceptions(() -> { - Repository repository = getRepository(participationId, RepositoryActionType.READ, true); - var nonFolderFileNames = super.repositoryService.getFiles(repository).entrySet().stream().filter(mapEntry -> mapEntry.getValue().equals(FileType.FILE)) - .map(Map.Entry::getKey).collect(Collectors.toSet()); - - return new ResponseEntity<>(nonFolderFileNames, HttpStatus.OK); - }); - } - @Override @PostMapping(value = "repository/{participationId}/file", produces = MediaType.APPLICATION_JSON_VALUE) @FeatureToggle(Feature.ProgrammingExercises) diff --git a/src/main/resources/templates/aeolus/java/plain_gradle_static_coverage.sh b/src/main/resources/templates/aeolus/java/plain_gradle_static_coverage.sh deleted file mode 100644 index ee2bfdea3d9b..000000000000 --- a/src/main/resources/templates/aeolus/java/plain_gradle_static_coverage.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -set -e -export AEOLUS_INITIAL_DIRECTORY=${PWD} -tests () { - echo '⚙️ executing tests' - chmod +x ./gradlew - ./gradlew clean test tiaTests --run-all-tests -} - -static_code_analysis () { - echo '⚙️ executing static_code_analysis' - ./gradlew check -x test -} - -final_aeolus_post_action () { - set +e # from now on, we don't exit on errors - echo '⚙️ executing final_aeolus_post_action' - cd "${AEOLUS_INITIAL_DIRECTORY}" - static_code_analysis -} - -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} - trap final_aeolus_post_action EXIT - - cd "${AEOLUS_INITIAL_DIRECTORY}" - bash -c "source ${_script_name} aeolus_sourcing; tests" -} - -main "${@}" diff --git a/src/main/resources/templates/aeolus/java/plain_gradle_static_coverage.yaml b/src/main/resources/templates/aeolus/java/plain_gradle_static_coverage.yaml deleted file mode 100644 index d9d319f2e49f..000000000000 --- a/src/main/resources/templates/aeolus/java/plain_gradle_static_coverage.yaml +++ /dev/null @@ -1,29 +0,0 @@ -api: v0.0.1 -actions: - - name: tests - script: |- - chmod +x ./gradlew - ./gradlew clean test tiaTests --run-all-tests - runAlways: false - - name: static_code_analysis - script: ./gradlew check -x test - runAlways: true - results: - - name: spotbugs - path: target/spotbugsXml.xml - type: static-code-analysis - - name: checkstyle - path: target/checkstyle-result.xml - type: static-code-analysis - - name: pmd - path: target/pmd.xml - type: static-code-analysis - - name: pmd_cpd - path: target/cpd.xml - type: static-code-analysis - - name: testwiseCoverageReport - path: build/reports/testwise-coverage/tiaTests/tiaTests.json - type: testwise-coverage - - name: junit_**/test-results/test/*.xml - path: '**/test-results/test/*.xml' - type: junit diff --git a/src/main/resources/templates/aeolus/java/plain_maven_static_coverage.sh b/src/main/resources/templates/aeolus/java/plain_maven_static_coverage.sh deleted file mode 100644 index d74afc686999..000000000000 --- a/src/main/resources/templates/aeolus/java/plain_maven_static_coverage.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash -set -e -export AEOLUS_INITIAL_DIRECTORY=${PWD} -maven () { - echo '⚙️ executing maven' - mvn clean test -Pcoverage -} - -move_report_file () { - echo '⚙️ executing move_report_file' - mv target/tia/reports/*/testwise-coverage-*.json target/tia/reports/tiaTests.json -} - -maven_1 () { - echo '⚙️ executing maven_1' - mvn spotbugs:spotbugs checkstyle:checkstyle pmd:pmd pmd:cpd -} - -final_aeolus_post_action () { - set +e # from now on, we don't exit on errors - echo '⚙️ executing final_aeolus_post_action' - cd "${AEOLUS_INITIAL_DIRECTORY}" - maven_1 -} - -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} - trap final_aeolus_post_action EXIT - - cd "${AEOLUS_INITIAL_DIRECTORY}" - bash -c "source ${_script_name} aeolus_sourcing; maven" - cd "${AEOLUS_INITIAL_DIRECTORY}" - bash -c "source ${_script_name} aeolus_sourcing; move_report_file" -} - -main "${@}" diff --git a/src/main/resources/templates/aeolus/java/plain_maven_static_coverage.yaml b/src/main/resources/templates/aeolus/java/plain_maven_static_coverage.yaml deleted file mode 100644 index 5017a23d8537..000000000000 --- a/src/main/resources/templates/aeolus/java/plain_maven_static_coverage.yaml +++ /dev/null @@ -1,30 +0,0 @@ -api: v0.0.1 -actions: - - name: maven - script: mvn clean test -Pcoverage - runAlways: false - - name: move_report_file - script: mv target/tia/reports/*/testwise-coverage-*.json target/tia/reports/tiaTests.json - runAlways: false - - name: maven - script: mvn spotbugs:spotbugs checkstyle:checkstyle pmd:pmd pmd:cpd - runAlways: true - results: - - name: spotbugs - path: target/spotbugsXml.xml - type: static-code-analysis - - name: checkstyle - path: target/checkstyle-result.xml - type: static-code-analysis - - name: pmd - path: target/pmd.xml - type: static-code-analysis - - name: pmd_cpd - path: target/cpd.xml - type: static-code-analysis - - name: testwiseCoverageReport - path: target/tia/reports/tiaTests.json - type: testwise-coverage - - name: junit - path: '**/target/surefire-reports/*.xml' - type: junit diff --git a/src/main/resources/templates/aeolus/kotlin/default_coverage.sh b/src/main/resources/templates/aeolus/kotlin/default_coverage.sh deleted file mode 100644 index 1840f600e00c..000000000000 --- a/src/main/resources/templates/aeolus/kotlin/default_coverage.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -set -e -export AEOLUS_INITIAL_DIRECTORY=${PWD} -maven () { - echo '⚙️ executing maven' - mvn clean test -Pcoverage -} - -move_report_file () { - echo '⚙️ executing move_report_file' - mv target/tia/reports/*/testwise-coverage-*.json target/tia/reports/tiaTests.json -} - -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; maven" - cd "${AEOLUS_INITIAL_DIRECTORY}" - bash -c "source ${_script_name} aeolus_sourcing; move_report_file" -} - -main "${@}" diff --git a/src/main/resources/templates/aeolus/kotlin/default_coverage.yaml b/src/main/resources/templates/aeolus/kotlin/default_coverage.yaml deleted file mode 100644 index 5f2c972b6223..000000000000 --- a/src/main/resources/templates/aeolus/kotlin/default_coverage.yaml +++ /dev/null @@ -1,15 +0,0 @@ -api: v0.0.1 -actions: - - name: maven - script: mvn clean test -Pcoverage - runAlways: false - - name: move_report_file - script: mv target/tia/reports/*/testwise-coverage-*.json target/tia/reports/tiaTests.json - runAlways: false - results: - - name: testwiseCoverageReport - path: target/tia/reports/tiaTests.json - before: false - - name: junit - path: '**/target/surefire-reports/*.xml' - type: junit diff --git a/src/main/resources/templates/iris/hestia.hbs b/src/main/resources/templates/iris/hestia.hbs deleted file mode 100644 index 615534e452a7..000000000000 --- a/src/main/resources/templates/iris/hestia.hbs +++ /dev/null @@ -1,80 +0,0 @@ -{{#system~}}# Your task -You are an assistant to generate long and short descriptions for code hints. -Code hints are hints for programming exercises. -Code hints contain a part of the sample solution code that is needed to solve the exercise. -Code hints contain a short description of the code hint, which describes the purpose of the code hint. -Code hints contain a long description of the code hint, which describes the solution of the hint. -Code hints are made available to the student automatically if they have trouble solving the exercise. -The short description of the code hint is displayed to the student as soon as it is available. -The long description and solution code of the code hint is displayed to the student if they decide to use the code hint. -Your task is to generate the short and long description of a code hint. - -# Context -This is the context of the exercise and code hint that you can use to generate the short and long description. -## Programming exercise -Information about the programming exercise. -### Title -{{exercise.title}} -### Problem statement -```markdown -{{exercise.problemStatement}} -``` -## Code hint -Information about the code hint. -### Title -{{codeHint.title}} -### Solution code -{{#each codeHint.solutionEntries}} -#### {{this.filePath}} -{{#if (contains this "previousLine")}} -**Previous line:** {{this.previousLine}} -**Previous code:** -``` -{{this.previousCode}} -``` -{{/if}} -**New Line:** {{this.line}} -**New code:** -``` -{{this.code}} -``` -{{~/each}} - -## Previous attempts -These are the previous attempts at creating descriptions for this code hint. -The instructor did not like the previous attempts and wants you to generate new and different descriptions. -{{~/system}} -{{#if (contains session "messages")}} - {{#each session.messages}} - {{#if (equal this.sender "LLM")}} - {{#if (equal this.content[0].type "json")}} - {{#assistant~}} - **Short description:** - {{this.content[0].attributes.shortDescription}} - **Long description:** - {{this.content[0].attributes.longDescription}} - {{~/assistant}} - {{/if}} - {{#if (equal this.content[0].type "text")}} - {{#assistant~}} - {{this.content[0].textContent}} - {{~/assistant}} - {{/if}} - {{/if}} - {{~/each}} -{{/if}} -{{#system~}} - -# Your solution -Create your solution here. You can use any markdown you want -## Short description -{{~/system}} -{{#assistant~}} -{{gen 'shortDescription' temperature=0.2 max_tokens=100 stop="\\n"}} -{{~/assistant}} -{{#system~}} -## Long description -{{~/system}} -{{#assistant~}} -{{gen 'longDescription' temperature=0.2 max_tokens=1000}} -{{~/assistant}} diff --git a/src/main/resources/templates/java/maven_maven/test/projectTemplate/pom.xml b/src/main/resources/templates/java/maven_maven/test/projectTemplate/pom.xml index 997728be058b..93720b6f3aee 100644 --- a/src/main/resources/templates/java/maven_maven/test/projectTemplate/pom.xml +++ b/src/main/resources/templates/java/maven_maven/test/projectTemplate/pom.xml @@ -88,14 +88,6 @@ ${project.build.outputDirectory}/sun/ ${project.build.outputDirectory}/org/gradle/ ${project.build.outputDirectory}/worker/org/gradle/ - - ${project.build.outputDirectory}/com/squareup/ - ${project.build.outputDirectory}/com/teamscale/ - ${project.build.outputDirectory}/okhttp3/ - ${project.build.outputDirectory}/okio/ - ${project.build.outputDirectory}/retrofit2/ - ${project.build.outputDirectory}/shadow/ - @@ -180,54 +172,4 @@ - - - - coverage - - - com.teamscale - impacted-test-engine - 33.1.2 - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.2.5 - - - okhttp3,com.teamscale,retrofit2,shadow,com.squareup,okio - - - - - com.teamscale - teamscale-maven-plugin - 33.1.2 - - http://localhost - dummy - ${exerciseNamePomXml}-Tests - dummy - testwise - - *${packageName}* - - - - - - prepare-tia-unit-test - - - - - - - - - diff --git a/src/main/resources/templates/java/test/gradle/projectTemplate/build.gradle b/src/main/resources/templates/java/test/gradle/projectTemplate/build.gradle index 97be1ab67bfd..b099c0b33b67 100644 --- a/src/main/resources/templates/java/test/gradle/projectTemplate/build.gradle +++ b/src/main/resources/templates/java/test/gradle/projectTemplate/build.gradle @@ -4,9 +4,7 @@ plugins { id 'pmd' id 'com.github.spotbugs' version '6.0.9' // %static-code-analysis-stop% - // %record-testwise-coverage-start% id 'com.teamscale' version '33.1.2' - // %record-testwise-coverage-stop% } apply plugin: 'java' @@ -97,14 +95,6 @@ def forbiddenPackageFolders = [ //(2) "$studentOutputDir/org/opentest4j/", "$studentOutputDir/sun/", "$studentOutputDir/worker/org/gradle/" - // %record-testwise-coverage-start% - ,"$studentOutputDir/com/teamscale/", - "$studentOutputDir/okhttp3/", - "$studentOutputDir/retrofit2/", - "$studentOutputDir/shadow/", - "$studentOutputDir/com/squareup/", - "$studentOutputDir/okio/" - // %record-testwise-coverage-stop% ] test { doFirst { //(1) @@ -178,20 +168,3 @@ pmd { } } // %static-code-analysis-stop% - - -// %record-testwise-coverage-start% -tasks.register('tiaTests', com.teamscale.TestImpacted) { - systemProperty "ares.security.trustedpackages", "okhttp3,com.teamscale,retrofit2,shadow,com.squareup,okio" - useJUnitPlatform() - filter { - excludeTestsMatching "AttributeTest" - excludeTestsMatching "ConstructorTest" - excludeTestsMatching "ClassTest" - excludeTestsMatching "MethodTest" - } - jacoco { - includes = ["${packageName}.*"] - } -} -// %record-testwise-coverage-stop% diff --git a/src/main/resources/templates/java/test/maven/projectTemplate/pom.xml b/src/main/resources/templates/java/test/maven/projectTemplate/pom.xml index c1a7d42fd3e8..24f9561bc6e9 100644 --- a/src/main/resources/templates/java/test/maven/projectTemplate/pom.xml +++ b/src/main/resources/templates/java/test/maven/projectTemplate/pom.xml @@ -83,14 +83,6 @@ ${project.build.outputDirectory}/sun/ ${project.build.outputDirectory}/org/gradle/ ${project.build.outputDirectory}/worker/org/gradle/ - - ${project.build.outputDirectory}/com/squareup/ - ${project.build.outputDirectory}/com/teamscale/ - ${project.build.outputDirectory}/okhttp3/ - ${project.build.outputDirectory}/okio/ - ${project.build.outputDirectory}/retrofit2/ - ${project.build.outputDirectory}/shadow/ - @@ -175,54 +167,4 @@ - - - - coverage - - - com.teamscale - impacted-test-engine - 33.1.2 - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.2.5 - - - okhttp3,com.teamscale,retrofit2,shadow,com.squareup,okio - - - - - com.teamscale - teamscale-maven-plugin - 33.1.2 - - http://localhost - dummy - ${exerciseNamePomXml}-Tests - dummy - testwise - - *${packageName}* - - - - - - prepare-tia-unit-test - - - - - - - - - diff --git a/src/main/resources/templates/jenkins/java/gradle/regularRuns/pipeline.groovy b/src/main/resources/templates/jenkins/java/gradle/regularRuns/pipeline.groovy index b83202251c06..2478c6a8fe40 100644 --- a/src/main/resources/templates/jenkins/java/gradle/regularRuns/pipeline.groovy +++ b/src/main/resources/templates/jenkins/java/gradle/regularRuns/pipeline.groovy @@ -12,7 +12,6 @@ dockerImage = '#dockerImage' dockerFlags = '#dockerArgs' isSolutionBuild = "${env.JOB_NAME}" ==~ /.+-SOLUTION$/ -isTestWiseCoverageEnabled = #testWiseCoverage && isSolutionBuild isStaticCodeAnalysisEnabled = #isStaticCodeAnalysisEnabled /** @@ -37,11 +36,7 @@ private void runTestSteps() { */ void test() { stage('Test') { - if (isTestWiseCoverageEnabled) { - sh './gradlew clean test tiaTests --run-all-tests' - } else { - sh './gradlew clean test' - } + sh './gradlew clean test' } } @@ -66,25 +61,12 @@ private void staticCodeAnalysis() { } } -private void collectTestwiseCoverageReport() { - catchError { - sh ''' - rm -rf testwiseCoverageReport - mkdir testwiseCoverageReport - mv build/reports/testwise-coverage/tiaTests/tiaTests.json testwiseCoverageReport/ - ''' - } -} - /** * Script of the post build tasks aggregating all JUnit files in $WORKSPACE/results. * * Called by Jenkins. */ void postBuildTasks() { - if (isTestWiseCoverageEnabled) { - collectTestwiseCoverageReport() - } sh ''' rm -rf results mkdir results diff --git a/src/main/resources/templates/jenkins/java/maven/regularRuns/pipeline.groovy b/src/main/resources/templates/jenkins/java/maven/regularRuns/pipeline.groovy index cb76cbb4ee84..326b5897fbaa 100644 --- a/src/main/resources/templates/jenkins/java/maven/regularRuns/pipeline.groovy +++ b/src/main/resources/templates/jenkins/java/maven/regularRuns/pipeline.groovy @@ -12,7 +12,6 @@ dockerImage = '#dockerImage' dockerFlags = '#dockerArgs' isSolutionBuild = "${env.JOB_NAME}" ==~ /.+-SOLUTION$/ -isTestwiseCoverageEnabled = #testWiseCoverage && isSolutionBuild isStaticCodeAnalysisEnabled = #isStaticCodeAnalysisEnabled /** @@ -37,11 +36,7 @@ private void runTestSteps() { */ private void test() { stage('Test') { - if (isTestwiseCoverageEnabled) { - sh 'mvn clean test -B -Pcoverage' - } else { - sh 'mvn clean test -B' - } + sh 'mvn clean test -B' } } @@ -66,25 +61,12 @@ private void staticCodeAnalysis() { } } -private void collectTestwiseCoverageReport() { - catchError { - sh ''' - rm -rf testwiseCoverageReport - mkdir testwiseCoverageReport - mv target/tia/reports/*/*.json testwiseCoverageReport/ - ''' - } -} - /** * Script of the post build tasks aggregating all JUnit files in $WORKSPACE/results. * * Called by Jenkins. */ void postBuildTasks() { - if (isTestwiseCoverageEnabled) { - collectTestwiseCoverageReport() - } sh ''' rm -rf results mkdir results diff --git a/src/main/resources/templates/jenkins/kotlin/regularRuns/pipeline.groovy b/src/main/resources/templates/jenkins/kotlin/regularRuns/pipeline.groovy index cb76cbb4ee84..326b5897fbaa 100644 --- a/src/main/resources/templates/jenkins/kotlin/regularRuns/pipeline.groovy +++ b/src/main/resources/templates/jenkins/kotlin/regularRuns/pipeline.groovy @@ -12,7 +12,6 @@ dockerImage = '#dockerImage' dockerFlags = '#dockerArgs' isSolutionBuild = "${env.JOB_NAME}" ==~ /.+-SOLUTION$/ -isTestwiseCoverageEnabled = #testWiseCoverage && isSolutionBuild isStaticCodeAnalysisEnabled = #isStaticCodeAnalysisEnabled /** @@ -37,11 +36,7 @@ private void runTestSteps() { */ private void test() { stage('Test') { - if (isTestwiseCoverageEnabled) { - sh 'mvn clean test -B -Pcoverage' - } else { - sh 'mvn clean test -B' - } + sh 'mvn clean test -B' } } @@ -66,25 +61,12 @@ private void staticCodeAnalysis() { } } -private void collectTestwiseCoverageReport() { - catchError { - sh ''' - rm -rf testwiseCoverageReport - mkdir testwiseCoverageReport - mv target/tia/reports/*/*.json testwiseCoverageReport/ - ''' - } -} - /** * Script of the post build tasks aggregating all JUnit files in $WORKSPACE/results. * * Called by Jenkins. */ void postBuildTasks() { - if (isTestwiseCoverageEnabled) { - collectTestwiseCoverageReport() - } sh ''' rm -rf results mkdir results diff --git a/src/main/resources/templates/kotlin/test/maven/projectTemplate/pom.xml b/src/main/resources/templates/kotlin/test/maven/projectTemplate/pom.xml index e278d59eb3d2..c1c04e44339e 100644 --- a/src/main/resources/templates/kotlin/test/maven/projectTemplate/pom.xml +++ b/src/main/resources/templates/kotlin/test/maven/projectTemplate/pom.xml @@ -138,14 +138,6 @@ ${project.build.outputDirectory}/sun/ ${project.build.outputDirectory}/org/gradle/ ${project.build.outputDirectory}/worker/org/gradle/ - - ${project.build.outputDirectory}/com/squareup/ - ${project.build.outputDirectory}/com/teamscale/ - ${project.build.outputDirectory}/okhttp3/ - ${project.build.outputDirectory}/okio/ - ${project.build.outputDirectory}/retrofit2/ - ${project.build.outputDirectory}/shadow/ - @@ -153,54 +145,4 @@ - - - - coverage - - - com.teamscale - impacted-test-engine - ${teamscale.version} - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.2.5 - - - okhttp3,com.teamscale,retrofit2,shadow,com.squareup,okio - - - - - com.teamscale - teamscale-maven-plugin - ${teamscale.version} - - http://localhost - dummy - ${exerciseNamePomXml}-Tests - dummy - testwise - - *${packageName}* - - - - - - prepare-tia-unit-test - - - - - - - - - diff --git a/src/main/webapp/app/course/manage/course-management.module.ts b/src/main/webapp/app/course/manage/course-management.module.ts index 12d386095e31..68b0ce410db3 100644 --- a/src/main/webapp/app/course/manage/course-management.module.ts +++ b/src/main/webapp/app/course/manage/course-management.module.ts @@ -20,7 +20,6 @@ import { ArtemisLectureModule } from 'app/lecture/lecture.module'; import { ArtemisTextExerciseManagementModule } from 'app/exercises/text/manage/text-exercise-management.module'; import { ArtemisDashboardsModule } from 'app/shared/dashboards/dashboards.module'; import { ArtemisParticipationModule } from 'app/exercises/shared/participation/participation.module'; -import { ArtemisExerciseHintManagementModule } from 'app/exercises/shared/exercise-hint/manage/exercise-hint-management.module'; import { ArtemisModelingExerciseManagementModule } from 'app/exercises/modeling/manage/modeling-exercise-management.module'; import { ArtemisCourseScoresModule } from 'app/course/course-scores/course-scores.module'; import { ArtemisExerciseScoresModule } from 'app/exercises/shared/exercise-scores/exercise-scores.module'; @@ -95,7 +94,6 @@ import { ArtemisMarkdownEditorModule } from 'app/shared/markdown-editor/markdown ArtemisModelingExerciseModule, ArtemisColorSelectorModule, ArtemisDashboardsModule, - ArtemisExerciseHintManagementModule, ArtemisParticipationModule, ArtemisComplaintsForTutorModule, ArtemisListOfComplaintsModule, diff --git a/src/main/webapp/app/course/manage/course-update.component.ts b/src/main/webapp/app/course/manage/course-update.component.ts index fd7c49954ea4..8922497168dd 100644 --- a/src/main/webapp/app/course/manage/course-update.component.ts +++ b/src/main/webapp/app/course/manage/course-update.component.ts @@ -128,7 +128,7 @@ export class CourseUpdateComponent implements OnInit { this.course.maxComplaintResponseTextLimit! > 0; this.requestMoreFeedbackEnabled = this.course.maxRequestMoreFeedbackTimeDays! > 0; } else { - this.fileService.getTemplateCodeOfCondcut().subscribe({ + this.fileService.getTemplateCodeOfConduct().subscribe({ next: (res: HttpResponse) => { if (res.body) { this.course.courseInformationSharingMessagingCodeOfConduct = res.body; diff --git a/src/main/webapp/app/course/manage/detail/course-detail.component.ts b/src/main/webapp/app/course/manage/detail/course-detail.component.ts index c6a3e11211ba..4b5ecc80b629 100644 --- a/src/main/webapp/app/course/manage/detail/course-detail.component.ts +++ b/src/main/webapp/app/course/manage/detail/course-detail.component.ts @@ -222,12 +222,6 @@ export class CourseDetailComponent implements OnInit, OnDestroy { data: { course: this.course, disabled: !this.isAdmin, subSettingsType: IrisSubSettingsType.CHAT }, }); } - // TODO: Enable in future PR - // details.push({ - // type: DetailType.ProgrammingIrisEnabled, - // title: 'artemisApp.iris.settings.subSettings.enabled.hesita', - // data: { course: this.course, disabled: !this.isAdmin, subSettingsType: this.HESTIA }, - // }); return irisDetails; } diff --git a/src/main/webapp/app/detail-overview-list/components/programming-diff-report-detail/programming-diff-report-detail.component.ts b/src/main/webapp/app/detail-overview-list/components/programming-diff-report-detail/programming-diff-report-detail.component.ts index 94fb86848f4f..46999aa24a27 100644 --- a/src/main/webapp/app/detail-overview-list/components/programming-diff-report-detail/programming-diff-report-detail.component.ts +++ b/src/main/webapp/app/detail-overview-list/components/programming-diff-report-detail/programming-diff-report-detail.component.ts @@ -3,12 +3,12 @@ import type { ProgrammingDiffReportDetail } from 'app/detail-overview-list/detai import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { ButtonSize, ButtonType, TooltipPlacement } from 'app/shared/components/button.component'; import { faCodeCompare } from '@fortawesome/free-solid-svg-icons'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; -import { GitDiffReportModalComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-report-modal.component'; +import { ProgrammingExerciseGitDiffReport } from 'app/entities/programming-exercise-git-diff-report.model'; +import { GitDiffReportModalComponent } from 'app/exercises/programming/git-diff-report/git-diff-report-modal.component'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { GitDiffLineStatComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component'; +import { GitDiffLineStatComponent } from 'app/exercises/programming/git-diff-report/git-diff-line-stat.component'; @Component({ selector: 'jhi-programming-diff-report-detail', diff --git a/src/main/webapp/app/detail-overview-list/detail.model.ts b/src/main/webapp/app/detail-overview-list/detail.model.ts index b34f408ef435..995f32024195 100644 --- a/src/main/webapp/app/detail-overview-list/detail.model.ts +++ b/src/main/webapp/app/detail-overview-list/detail.model.ts @@ -5,7 +5,7 @@ import { SolutionProgrammingExerciseParticipation } from 'app/entities/participa import { ProgrammingExerciseInstructorRepositoryType } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; import { ProgrammingExerciseParticipationType } from 'app/entities/programming/programming-exercise-participation.model'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffReport } from 'app/entities/programming-exercise-git-diff-report.model'; import { BuildLogStatisticsDTO } from 'app/entities/programming/build-log-statistics-dto'; import { DetailType } from 'app/detail-overview-list/detail-overview-list.component'; import { SafeHtml } from '@angular/platform-browser'; diff --git a/src/main/webapp/app/entities/hestia/code-hint-model.ts b/src/main/webapp/app/entities/hestia/code-hint-model.ts deleted file mode 100644 index 310a0a5839a2..000000000000 --- a/src/main/webapp/app/entities/hestia/code-hint-model.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; - -export enum CodeHintGenerationStep { - GIT_DIFF, - COVERAGE, - SOLUTION_ENTRIES, - CODE_HINTS, -} -export class CodeHint extends ExerciseHint { - public solutionEntries?: ProgrammingExerciseSolutionEntry[]; -} diff --git a/src/main/webapp/app/entities/hestia/coverage-file-report.model.ts b/src/main/webapp/app/entities/hestia/coverage-file-report.model.ts deleted file mode 100644 index 03c3b278b6e6..000000000000 --- a/src/main/webapp/app/entities/hestia/coverage-file-report.model.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { BaseEntity } from 'app/shared/model/base-entity'; -import { TestwiseCoverageReportEntry } from 'app/entities/hestia/testwise-coverage-report-entry.model'; - -export class CoverageFileReport implements BaseEntity { - public id?: number; - - public filePath?: string; - public lineCount?: number; - public coveredLineCount?: number; - public testwiseCoverageEntries?: TestwiseCoverageReportEntry[]; - - constructor() {} -} diff --git a/src/main/webapp/app/entities/hestia/coverage-report.model.ts b/src/main/webapp/app/entities/hestia/coverage-report.model.ts deleted file mode 100644 index c60906c135d0..000000000000 --- a/src/main/webapp/app/entities/hestia/coverage-report.model.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BaseEntity } from 'app/shared/model/base-entity'; -import { CoverageFileReport } from 'app/entities/hestia/coverage-file-report.model'; - -export class CoverageReport implements BaseEntity { - public id?: number; - - public fileReports?: CoverageFileReport[]; - public coveredLineRatio?: number; - - constructor() {} -} diff --git a/src/main/webapp/app/entities/hestia/exercise-hint.model.ts b/src/main/webapp/app/entities/hestia/exercise-hint.model.ts deleted file mode 100644 index fa61b885b7ec..000000000000 --- a/src/main/webapp/app/entities/hestia/exercise-hint.model.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { BaseEntity } from 'app/shared/model/base-entity'; -import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; - -export enum HintType { - TEXT = 'text', - CODE = 'code', -} - -export class ExerciseHint implements BaseEntity { - public id?: number; - public title?: string; - public description?: string; - public content?: string; - public exercise?: ProgrammingExercise; - public type?: HintType; - public programmingExerciseTask?: ProgrammingExerciseServerSideTask; - public displayThreshold?: number; - public currentUserRating?: number; -} diff --git a/src/main/webapp/app/entities/hestia/programming-exercise-solution-entry.model.ts b/src/main/webapp/app/entities/hestia/programming-exercise-solution-entry.model.ts deleted file mode 100644 index 9380ec858a4f..000000000000 --- a/src/main/webapp/app/entities/hestia/programming-exercise-solution-entry.model.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BaseEntity } from 'app/shared/model/base-entity'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; -import { CodeHint } from 'app/entities/hestia/code-hint-model'; - -export class ProgrammingExerciseSolutionEntry implements BaseEntity { - id?: number; - filePath?: string; - previousLine?: number; - line?: number; - previousCode?: string; - code?: string; - testCase?: ProgrammingExerciseTestCase; - codeHint?: CodeHint; -} diff --git a/src/main/webapp/app/entities/hestia/testwise-coverage-report-entry.model.ts b/src/main/webapp/app/entities/hestia/testwise-coverage-report-entry.model.ts deleted file mode 100644 index 59234fffbcfe..000000000000 --- a/src/main/webapp/app/entities/hestia/testwise-coverage-report-entry.model.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { BaseEntity } from 'app/shared/model/base-entity'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; - -export class TestwiseCoverageReportEntry implements BaseEntity { - public id?: number; - - public startLine?: number; - public lineCount?: number; - public testCase?: ProgrammingExerciseTestCase; - - constructor() {} -} diff --git a/src/main/webapp/app/entities/hestia/programming-exercise-git-diff-entry.model.ts b/src/main/webapp/app/entities/programming-exercise-git-diff-entry.model.ts similarity index 100% rename from src/main/webapp/app/entities/hestia/programming-exercise-git-diff-entry.model.ts rename to src/main/webapp/app/entities/programming-exercise-git-diff-entry.model.ts diff --git a/src/main/webapp/app/entities/hestia/programming-exercise-git-diff-report.model.ts b/src/main/webapp/app/entities/programming-exercise-git-diff-report.model.ts similarity index 85% rename from src/main/webapp/app/entities/hestia/programming-exercise-git-diff-report.model.ts rename to src/main/webapp/app/entities/programming-exercise-git-diff-report.model.ts index 5fccf4e2f4aa..d688911d1ccd 100644 --- a/src/main/webapp/app/entities/hestia/programming-exercise-git-diff-report.model.ts +++ b/src/main/webapp/app/entities/programming-exercise-git-diff-report.model.ts @@ -1,6 +1,6 @@ import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { BaseEntity } from 'app/shared/model/base-entity'; -import { ProgrammingExerciseGitDiffEntry } from 'app/entities/hestia/programming-exercise-git-diff-entry.model'; +import { ProgrammingExerciseGitDiffEntry } from 'app/entities/programming-exercise-git-diff-entry.model'; export class ProgrammingExerciseGitDiffReport implements BaseEntity { public id?: number; diff --git a/src/main/webapp/app/entities/hestia/programming-exercise-task.model.ts b/src/main/webapp/app/entities/programming-exercise-task.model.ts similarity index 100% rename from src/main/webapp/app/entities/hestia/programming-exercise-task.model.ts rename to src/main/webapp/app/entities/programming-exercise-task.model.ts diff --git a/src/main/webapp/app/entities/programming/build-config.model.ts b/src/main/webapp/app/entities/programming/build-config.model.ts index dcae29ec591f..af71acf37afe 100644 --- a/src/main/webapp/app/entities/programming/build-config.model.ts +++ b/src/main/webapp/app/entities/programming/build-config.model.ts @@ -6,6 +6,5 @@ export class BuildConfig { public projectType?: string; public scaEnabled?: boolean; public sequentialTestRunsEnabled?: boolean; - public testwiseCoverageEnabled?: boolean; public resultPaths?: string[]; } diff --git a/src/main/webapp/app/entities/programming/programming-exercise-build.config.ts b/src/main/webapp/app/entities/programming/programming-exercise-build.config.ts index 55e5f5f487e2..d82e5f345c4d 100644 --- a/src/main/webapp/app/entities/programming/programming-exercise-build.config.ts +++ b/src/main/webapp/app/entities/programming/programming-exercise-build.config.ts @@ -11,11 +11,9 @@ export class ProgrammingExerciseBuildConfig { public timeoutSeconds?: number; public dockerFlags?: string; public windfile?: WindFile; - public testwiseCoverageEnabled?: boolean; public theiaImage?: string; constructor() { this.checkoutSolutionRepository = false; // default value - this.testwiseCoverageEnabled = false; // default value } } diff --git a/src/main/webapp/app/entities/programming/programming-exercise-test-case.model.ts b/src/main/webapp/app/entities/programming/programming-exercise-test-case.model.ts index 2d6822dc7ce4..5d43e28c7d16 100644 --- a/src/main/webapp/app/entities/programming/programming-exercise-test-case.model.ts +++ b/src/main/webapp/app/entities/programming/programming-exercise-test-case.model.ts @@ -1,6 +1,5 @@ import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { BaseEntity } from 'app/shared/model/base-entity'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; import { TestCaseStats } from './programming-exercise-test-case-statistics.model'; export enum Visibility { @@ -25,7 +24,6 @@ export class ProgrammingExerciseTestCase implements BaseEntity { visibility?: Visibility; exercise?: ProgrammingExercise; type?: ProgrammingExerciseTestCaseType; - solutionEntries?: ProgrammingExerciseSolutionEntry[]; // Utility information that is not actually part of the object stored on the server resultingPoints?: number; 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 646f7bdfdf4d..bd9246f8569e 100644 --- a/src/main/webapp/app/entities/programming/programming-exercise.model.ts +++ b/src/main/webapp/app/entities/programming/programming-exercise.model.ts @@ -2,8 +2,7 @@ import { AssessmentType } from 'app/entities/assessment-type.model'; import { Course } from 'app/entities/course.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { Exercise, ExerciseType, resetForImport } from 'app/entities/exercise.model'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffReport } from 'app/entities/programming-exercise-git-diff-report.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; import { BuildLogStatisticsDTO } from 'app/entities/programming/build-log-statistics-dto'; @@ -63,7 +62,6 @@ export class ProgrammingExercise extends Exercise { public showTestNamesToStudents?: boolean; public auxiliaryRepositories?: AuxiliaryRepository[]; public submissionPolicy?: SubmissionPolicy; - public exerciseHints?: ExerciseHint[]; public gitDiffReport?: ProgrammingExerciseGitDiffReport; public buildLogStatistics?: BuildLogStatisticsDTO; public buildConfig?: ProgrammingExerciseBuildConfig; @@ -74,11 +72,6 @@ export class ProgrammingExercise extends Exercise { public projectType?: ProjectType; - // helper attributes - - // this attribute is used to display the covered lines ratio - public coveredLinesRatio?: number; - /** * This attribute is used to generate a programming exercise with no connection to the VCS and CI. * This functionality is only for testing purposes. @@ -126,8 +119,6 @@ export function copyBuildConfigFromExerciseJson(exerciseJson: ProgrammingExercis buildConfig.timeoutSeconds = exerciseJson.timeoutSeconds ?? 0; buildConfig.windfile = exerciseJson.windfile ?? undefined; buildConfig.buildScript = exerciseJson.buildScript ?? ''; - buildConfig.testwiseCoverageEnabled = exerciseJson.testwiseCoverageEnabled ?? false; buildConfig.dockerFlags = exerciseJson.dockerFlags ?? ''; - return buildConfig; } diff --git a/src/main/webapp/app/exam/manage/exam-management.module.ts b/src/main/webapp/app/exam/manage/exam-management.module.ts index 42b34266ba2b..5590adf04e27 100644 --- a/src/main/webapp/app/exam/manage/exam-management.module.ts +++ b/src/main/webapp/app/exam/manage/exam-management.module.ts @@ -67,7 +67,7 @@ import { ArtemisProgrammingExerciseModule } from 'app/exercises/programming/shar import { DetailModule } from 'app/detail-overview-list/detail.module'; import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; import { NoDataComponent } from 'app/shared/no-data-component'; -import { GitDiffLineStatComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component'; +import { GitDiffLineStatComponent } from 'app/exercises/programming/git-diff-report/git-diff-line-stat.component'; const ENTITY_STATES = [...examManagementState]; diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component.ts b/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component.ts index 81a3f247a809..c3979797994f 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component.ts @@ -3,7 +3,7 @@ import { ProgrammingExercise } from 'app/entities/programming/programming-exerci import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { ButtonSize } from 'app/shared/components/button.component'; -import { GitDiffReportModalComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-report-modal.component'; +import { GitDiffReportModalComponent } from 'app/exercises/programming/git-diff-report/git-diff-report-modal.component'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { Exercise, IncludedInOverallScore } from 'app/entities/exercise.model'; @@ -11,7 +11,7 @@ import { ExamSubmissionComponent } from 'app/exam/participate/exercises/exam-sub import { Submission } from 'app/entities/submission.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { faCodeCompare } from '@fortawesome/free-solid-svg-icons'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffReport } from 'app/entities/programming-exercise-git-diff-report.model'; import { ExamPageComponent } from 'app/exam/participate/exercises/exam-page.component'; import { Observable, Subject, Subscription, debounceTime, take } from 'rxjs'; import { CachedRepositoryFilesService } from 'app/exercises/programming/manage/services/cached-repository-files.service'; diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component.ts b/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component.ts index 70f4cfe76710..ac7f501a52d5 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component.ts @@ -17,7 +17,7 @@ import { SubmissionVersionService } from 'app/exercises/shared/submission-versio import { ProgrammingExerciseExamDiffComponent } from 'app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component'; import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import { ExamPageComponent } from 'app/exam/participate/exercises/exam-page.component'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffReport } from 'app/entities/programming-exercise-git-diff-report.model'; @Component({ selector: 'jhi-student-exam-timeline', diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel-title.component.html b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel-title.component.html similarity index 100% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel-title.component.html rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel-title.component.html diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel-title.component.scss b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel-title.component.scss similarity index 100% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel-title.component.scss rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel-title.component.scss diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel-title.component.ts b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel-title.component.ts similarity index 100% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel-title.component.ts rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel-title.component.ts diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel.component.html b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component.html similarity index 100% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel.component.html rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component.html diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel.component.scss b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component.scss similarity index 100% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel.component.scss rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component.scss diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel.component.ts b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component.ts similarity index 90% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel.component.ts rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component.ts index 9e01edae26b9..0417e9239618 100644 --- a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file-panel.component.ts +++ b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component.ts @@ -1,9 +1,9 @@ import { ChangeDetectionStrategy, Component, ViewEncapsulation, computed, input, output } from '@angular/core'; -import { ProgrammingExerciseGitDiffEntry } from 'app/entities/hestia/programming-exercise-git-diff-entry.model'; +import { ProgrammingExerciseGitDiffEntry } from 'app/entities/programming-exercise-git-diff-entry.model'; import { faAngleDown, faAngleUp } from '@fortawesome/free-solid-svg-icons'; -import { GitDiffFilePanelTitleComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-file-panel-title.component'; -import { GitDiffLineStatComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component'; -import { GitDiffFileComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-file.component'; +import { GitDiffFilePanelTitleComponent } from 'app/exercises/programming/git-diff-report/git-diff-file-panel-title.component'; +import { GitDiffLineStatComponent } from 'app/exercises/programming/git-diff-report/git-diff-line-stat.component'; +import { GitDiffFileComponent } from 'app/exercises/programming/git-diff-report/git-diff-file.component'; import { ArtemisSharedModule } from 'app/shared/shared.module'; @Component({ diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file.component.html b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file.component.html similarity index 100% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file.component.html rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file.component.html diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file.component.ts b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file.component.ts similarity index 92% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file.component.ts rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file.component.ts index 45456a115513..055875de5a6a 100644 --- a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-file.component.ts +++ b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, ViewEncapsulation, computed, effect, input, output, viewChild } from '@angular/core'; -import { ProgrammingExerciseGitDiffEntry } from 'app/entities/hestia/programming-exercise-git-diff-entry.model'; +import { ProgrammingExerciseGitDiffEntry } from 'app/entities/programming-exercise-git-diff-entry.model'; import { MonacoDiffEditorComponent } from 'app/shared/monaco-editor/monaco-diff-editor.component'; import { TranslateDirective } from 'app/shared/language/translate.directive'; diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component.html b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component.html similarity index 100% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component.html rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component.html diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component.scss b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component.scss similarity index 100% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component.scss rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component.scss diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component.ts b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component.ts similarity index 100% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component.ts rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component.ts diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-report-modal.component.html b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report-modal.component.html similarity index 100% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-report-modal.component.html rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report-modal.component.html diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-report-modal.component.ts b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report-modal.component.ts similarity index 97% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-report-modal.component.ts rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report-modal.component.ts index 7684573f821f..96a16d6986ee 100644 --- a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-report-modal.component.ts +++ b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report-modal.component.ts @@ -1,11 +1,11 @@ import { ChangeDetectionStrategy, Component, effect, inject, input, signal, untracked } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffReport } from 'app/entities/programming-exercise-git-diff-report.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import { CachedRepositoryFilesService } from 'app/exercises/programming/manage/services/cached-repository-files.service'; import { firstValueFrom } from 'rxjs'; -import { GitDiffReportComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-report.component'; +import { GitDiffReportComponent } from 'app/exercises/programming/git-diff-report/git-diff-report.component'; import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-report.component.html b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report.component.html similarity index 100% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-report.component.html rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report.component.html diff --git a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-report.component.ts b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report.component.ts similarity index 95% rename from src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-report.component.ts rename to src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report.component.ts index cc916e5daf93..7edc4f32aaf3 100644 --- a/src/main/webapp/app/exercises/programming/hestia/git-diff-report/git-diff-report.component.ts +++ b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report.component.ts @@ -1,12 +1,12 @@ import { ChangeDetectionStrategy, Component, computed, effect, input, signal, untracked } from '@angular/core'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; -import { ProgrammingExerciseGitDiffEntry } from 'app/entities/hestia/programming-exercise-git-diff-entry.model'; +import { ProgrammingExerciseGitDiffReport } from 'app/entities/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffEntry } from 'app/entities/programming-exercise-git-diff-entry.model'; import { faSpinner, faTableColumns } from '@fortawesome/free-solid-svg-icons'; import { ButtonSize, ButtonType, TooltipPlacement } from 'app/shared/components/button.component'; -import { GitDiffLineStatComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component'; +import { GitDiffLineStatComponent } from 'app/exercises/programming/git-diff-report/git-diff-line-stat.component'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { GitDiffFilePanelComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-file-panel.component'; +import { GitDiffFilePanelComponent } from 'app/exercises/programming/git-diff-report/git-diff-file-panel.component'; interface DiffInformation { path: string; diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.html b/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.html deleted file mode 100644 index 9a62ddda158b..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.html +++ /dev/null @@ -1,52 +0,0 @@ -

- -

-
-@if (isPerformedByStep?.size && currentStep !== undefined && exercise) { -
-
-
- @if (allowBehavioralEntryGeneration ? currentStep !== 0 : currentStep !== 2) { - - } -
-
- -
-
- @if (!isNextStepAvailable()) { - - } - @if (currentStep !== 3) { - - } -
-
-
- - @if (exercise) { - @if (allowBehavioralEntryGeneration) { - - } - @if (allowBehavioralEntryGeneration) { - - } - - - } -
-} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.scss b/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.scss deleted file mode 100644 index 2fa141c0b0d4..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.scss +++ /dev/null @@ -1,49 +0,0 @@ -.button-row { - & > button { - margin-right: 5px; - } -} - -.status-wrapper { - flex-grow: 3; -} - -.butonene-wr .btn[disabled] { - pointer-events: all !important; -} - -.mat-expansion-panel.code-hint-creation-expansion-panel-wrapper, -.mat-expansion-panel.code-hint-creation-expansion-panel-wrapper-header { - .mat-expansion-panel-header { - padding: 10px 10px 10px 0; - border-bottom: 1px solid rgba(0, 0, 0, 0.25); - } - - .mat-expansion-panel-header-title, - .mat-expansion-panel-header-description { - flex-basis: 0; - } - - .mat-expansion-panel-header-description { - justify-content: space-between; - align-items: center; - } -} - -/* prevent resizing of opened expansion panels */ -.mat-expansion-panel-spacing { - margin: 0; -} - -.mat-expansion-panel.code-hint-creation-expansion-panel-wrapper-header { - font-weight: bold; - cursor: default; - - .mat-expansion-panel-header { - border-bottom: 2px solid; - } -} - -.sort-icon { - cursor: pointer; -} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.ts deleted file mode 100644 index d039db390a19..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { CoverageReport } from 'app/entities/hestia/coverage-report.model'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { CodeHint, CodeHintGenerationStep } from 'app/entities/hestia/code-hint-model'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; - -@Component({ - selector: 'jhi-code-hint-generation-overview', - templateUrl: './code-hint-generation-overview.component.html', - styleUrls: ['./code-hint-generation-overview.component.scss'], -}) -export class CodeHintGenerationOverviewComponent implements OnInit { - exercise?: ProgrammingExercise; - - currentStep: CodeHintGenerationStep; - isPerformedByStep: Map; - selectedSolutionEntries?: ProgrammingExerciseSolutionEntry[]; - - allowBehavioralEntryGeneration = false; - - readonly GenerationStep = CodeHintGenerationStep; - - constructor( - private route: ActivatedRoute, - private router: Router, - ) {} - - ngOnInit() { - this.route.data.subscribe(({ exercise }) => { - this.exercise = exercise; - // set all steps to unperformed initially - this.isPerformedByStep = new Map(); - this.isPerformedByStep.set(CodeHintGenerationStep.SOLUTION_ENTRIES, false); - this.isPerformedByStep.set(CodeHintGenerationStep.CODE_HINTS, false); - if (exercise.buildConfig?.testwiseCoverageEnabled) { - this.currentStep = CodeHintGenerationStep.GIT_DIFF; - this.allowBehavioralEntryGeneration = true; - this.isPerformedByStep.set(CodeHintGenerationStep.GIT_DIFF, false); - this.isPerformedByStep.set(CodeHintGenerationStep.COVERAGE, false); - } else { - this.currentStep = CodeHintGenerationStep.SOLUTION_ENTRIES; - } - }); - } - - setLatestPerformedStep(latestUpdatedStep: CodeHintGenerationStep) { - if (this.currentStep >= latestUpdatedStep) { - return; - } - const optionalEntry = Array.from(this.isPerformedByStep.entries()) - .filter((a) => a[1]) - .sort((a, b) => b[0] - a[0]) - .first(); - this.currentStep = optionalEntry === undefined ? this.currentStep : optionalEntry![0]; - } - - isNextStepAvailable(): boolean { - return this.isPerformedByStep.get(this.currentStep) ?? false; - } - - onNextStep() { - this.currentStep = this.currentStep + 1; - } - - onPreviousStep() { - this.currentStep = this.currentStep - 1; - } - - onStepChange(step: CodeHintGenerationStep) { - this.currentStep = step; - } - - onDiffReportLoaded(diffReport?: ProgrammingExerciseGitDiffReport) { - this.isPerformedByStep.set(CodeHintGenerationStep.GIT_DIFF, diffReport !== undefined); - this.setLatestPerformedStep(CodeHintGenerationStep.GIT_DIFF); - } - - onCoverageReportLoaded(coverageReport?: CoverageReport) { - this.isPerformedByStep.set(CodeHintGenerationStep.COVERAGE, coverageReport !== undefined); - this.setLatestPerformedStep(CodeHintGenerationStep.COVERAGE); - } - - onSolutionEntryChanges(entries?: ProgrammingExerciseSolutionEntry[]) { - this.selectedSolutionEntries = entries; - this.isPerformedByStep.set(CodeHintGenerationStep.SOLUTION_ENTRIES, entries !== undefined && entries!.length > 0); - this.setLatestPerformedStep(CodeHintGenerationStep.SOLUTION_ENTRIES); - } - - onCodeHintsLoaded(codeHints?: CodeHint[]) { - this.isPerformedByStep.set(CodeHintGenerationStep.CODE_HINTS, codeHints !== undefined && codeHints!.length > 0); - this.setLatestPerformedStep(CodeHintGenerationStep.CODE_HINTS); - } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.module.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.module.ts deleted file mode 100644 index a9d7088b56c9..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.module.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { NgModule } from '@angular/core'; -import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { CodeHintGenerationStatusComponent } from '../code-hint-generation-status/code-hint-generation-status.component'; -import { SolutionEntryDetailsModalComponent } from '../solution-entry-details-modal/solution-entry-details-modal.component'; -import { ArtemisExerciseHintSharedModule } from 'app/exercises/shared/exercise-hint/shared/exercise-hint-shared.module'; -import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; -import { DiffGenerationStepComponent } from '../steps/diff-generation-step/diff-generation-step.component'; -import { CoverageGenerationStepComponent } from '../steps/coverage-generation-step/coverage-generation-step.component'; -import { SolutionEntryGenerationStepComponent } from '../steps/solution-entry-generation-step/solution-entry-generation-step.component'; -import { CodeHintGenerationStepComponent } from '../steps/code-hint-generation-step/code-hint-generation-step.component'; -import { TestwiseCoverageReportModule } from 'app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.module'; -import { CodeHintGenerationOverviewComponent } from 'app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component'; -import { RouterModule } from '@angular/router'; -import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; -import { MatExpansionModule } from '@angular/material/expansion'; -import { CodeHintGenerationStatusStepComponent } from 'app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status-step.component'; -import { ManualSolutionEntryCreationModalComponent } from '../manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component'; -import { GitDiffReportComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-report.component'; - -@NgModule({ - imports: [ - RouterModule, - ArtemisSharedModule, - ArtemisExerciseHintSharedModule, - ArtemisSharedComponentModule, - TestwiseCoverageReportModule, - ArtemisMarkdownModule, - MatExpansionModule, - GitDiffReportComponent, - ], - declarations: [ - CodeHintGenerationStatusComponent, - SolutionEntryDetailsModalComponent, - DiffGenerationStepComponent, - CoverageGenerationStepComponent, - SolutionEntryGenerationStepComponent, - CodeHintGenerationStepComponent, - CodeHintGenerationOverviewComponent, - CodeHintGenerationStatusStepComponent, - ManualSolutionEntryCreationModalComponent, - ], - exports: [ - CodeHintGenerationStatusComponent, - SolutionEntryDetailsModalComponent, - DiffGenerationStepComponent, - CoverageGenerationStepComponent, - SolutionEntryGenerationStepComponent, - CodeHintGenerationStepComponent, - CodeHintGenerationOverviewComponent, - CodeHintGenerationStatusStepComponent, - ], -}) -export class ArtemisCodeHintGenerationOverviewModule {} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status-step.component.html b/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status-step.component.html deleted file mode 100644 index 0fb688bdaade..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status-step.component.html +++ /dev/null @@ -1,14 +0,0 @@ -
-
- @if (isPerformed) { - - } - @if (!isPerformed) { - - } -
-
- Current step - -
-
diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status-step.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status-step.component.ts deleted file mode 100644 index d46904d04367..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status-step.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { faCheck, faDotCircle } from '@fortawesome/free-solid-svg-icons'; - -@Component({ - selector: 'jhi-code-hint-generation-status-step', - templateUrl: './code-hint-generation-status-step.component.html', - styleUrls: ['./code-hint-generation-status.component.scss'], -}) -export class CodeHintGenerationStatusStepComponent { - @Input() - currentlySelected: boolean; - - @Input() - isPerformed: boolean; - - @Input() - labelTranslationKey: string; - - @Input() - descriptionTranslationKey: string; - - faCheck = faCheck; - faDotCircle = faDotCircle; -} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status.component.html b/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status.component.html deleted file mode 100644 index 6cc22ad5429d..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status.component.html +++ /dev/null @@ -1,45 +0,0 @@ -
- - @if (isPerformedByStep.get(GenerationStep.GIT_DIFF) !== undefined) { - - } - @if (isPerformedByStep.get(GenerationStep.GIT_DIFF) !== undefined) { -
- } - - @if (isPerformedByStep.get(GenerationStep.COVERAGE) !== undefined) { - - } - @if (isPerformedByStep.get(GenerationStep.COVERAGE) !== undefined) { -
- } - - -
- - -
diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status.component.scss b/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status.component.scss deleted file mode 100644 index 5a790263e647..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status.component.scss +++ /dev/null @@ -1,65 +0,0 @@ -.code-hint-generation-wrapper { - display: flex; - justify-content: space-between; - - .planned, - .finished, - .check { - color: var(--success); - } - - .unset { - color: var(--secondary); - } - - .current { - color: var(--warning); - } - - li { - display: list-item !important; - } - - .connector { - width: 100%; - min-width: 50px; - border-top: 2px solid; - margin-top: 1rem; - } -} - -.status-step { - width: 25px; - overflow-x: visible; - justify-content: flex-start; - display: flex; - flex-direction: column; - align-items: center; - - .status-step-content { - min-width: fit-content; - white-space: nowrap; - - .selected-label { - font-weight: bold; - } - } - - .header-icon { - z-index: 2; - font-size: 2em; - - & > fa-icon { - cursor: pointer; - background-color: var(--programming-exercise-instruction-step-wizard-card-header-background); - width: 30px; - height: 30px; - text-align: center; - padding: 6px 0; - font-size: 12px; - line-height: 1.428571429; - border-radius: 15px; - border-color: var(--programming-exercise-instruction-step-wizard-btn-border-color); - } - } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status.component.ts deleted file mode 100644 index b0c31d9f9676..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { CodeHintGenerationStep } from 'app/entities/hestia/code-hint-model'; - -@Component({ - selector: 'jhi-code-hint-generation-status', - templateUrl: './code-hint-generation-status.component.html', - styleUrls: ['./code-hint-generation-status.component.scss'], -}) -export class CodeHintGenerationStatusComponent { - @Input() - currentStep: CodeHintGenerationStep; - - @Input() - isPerformedByStep: Map; - - @Output() - onStepChange = new EventEmitter(); - - readonly GenerationStep = CodeHintGenerationStep; - - constructor() {} - - onSelectStep(step: CodeHintGenerationStep) { - this.onStepChange.emit(step); - } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component.html b/src/main/webapp/app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component.html deleted file mode 100644 index 842eb668da94..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component.html +++ /dev/null @@ -1,43 +0,0 @@ -
- - -
diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component.ts deleted file mode 100644 index 0df03f1664b3..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Component, EventEmitter, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { Subject } from 'rxjs'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { CodeHint } from 'app/entities/hestia/code-hint-model'; -import { ProgrammingExerciseSolutionEntryService } from 'app/exercises/shared/exercise-hint/services/programming-exercise-solution-entry.service'; -import { SolutionEntryComponent } from 'app/exercises/shared/exercise-hint/shared/solution-entry.component'; - -@Component({ - selector: 'jhi-manual-solution-entry-creation-modal', - templateUrl: './manual-solution-entry-creation-modal.component.html', -}) -export class ManualSolutionEntryCreationModalComponent implements OnInit, OnDestroy { - @ViewChild('solutionEntryComponent', { static: false }) solutionEntryComponent: SolutionEntryComponent; - - solutionEntry = new ProgrammingExerciseSolutionEntry(); - - exerciseId: number; - codeHint?: CodeHint; - testCases?: ProgrammingExerciseTestCase[]; - onEntryCreated = new EventEmitter(); - - solutionRepositoryFileNames?: string[]; - - private dialogErrorSource = new Subject(); - dialogError$ = this.dialogErrorSource.asObservable(); - - constructor( - private activeModal: NgbActiveModal, - private exerciseService: ProgrammingExerciseService, - private solutionEntryService: ProgrammingExerciseSolutionEntryService, - ) { - this.solutionEntry.code = ''; - this.solutionEntry.line = 1; - } - - ngOnDestroy(): void { - this.dialogErrorSource.unsubscribe(); - } - - ngOnInit(): void { - this.solutionEntry.codeHint = this.codeHint; - // only load test cases if not set as an input - if (!this.testCases) { - this.exerciseService.getAllTestCases(this.exerciseId).subscribe({ - next: (testCases) => { - this.testCases = testCases; - }, - error: (error) => this.dialogErrorSource.error(error.message), - }); - } - this.exerciseService.getSolutionFileNames(this.exerciseId).subscribe({ - next: (fileNames) => { - this.solutionRepositoryFileNames = fileNames; - }, - error: (error) => this.dialogErrorSource.error(error.message), - }); - } - - /** - * Updates the solution entry editor to refer to the correct file path. - * In particular, this updates the syntax highlighting of the editor. - */ - onUpdateFilePath(): void { - this.solutionEntryComponent.setupEditor(); - } - - clear() { - this.activeModal.close(); - } - - onCreateEntry() { - // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain - this.solutionEntryService.createSolutionEntry(this.exerciseId, this.solutionEntry?.testCase?.id!, this.solutionEntry).subscribe({ - next: (createdEntry) => { - this.dialogErrorSource.next(''); - this.onEntryCreated.emit(createdEntry); - this.activeModal.close(); - }, - error: (error) => this.dialogErrorSource.error(error.message), - }); - } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/solution-entry-details-modal/solution-entry-details-modal.component.html b/src/main/webapp/app/exercises/programming/hestia/generation-overview/solution-entry-details-modal/solution-entry-details-modal.component.html deleted file mode 100644 index 721b53f675b1..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/solution-entry-details-modal/solution-entry-details-modal.component.html +++ /dev/null @@ -1,21 +0,0 @@ -
- - -
diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/solution-entry-details-modal/solution-entry-details-modal.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/solution-entry-details-modal/solution-entry-details-modal.component.ts deleted file mode 100644 index 8e2dcbacb3ab..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/solution-entry-details-modal/solution-entry-details-modal.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Component, OnDestroy } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { Subject } from 'rxjs'; -import { ProgrammingExerciseSolutionEntryService } from 'app/exercises/shared/exercise-hint/services/programming-exercise-solution-entry.service'; - -@Component({ - selector: 'jhi-solution-entry-details-modal', - templateUrl: './solution-entry-details-modal.component.html', -}) -export class SolutionEntryDetailsModalComponent implements OnDestroy { - exerciseId: number; - solutionEntry: ProgrammingExerciseSolutionEntry; - isEditable: boolean; - - private dialogErrorSource = new Subject(); - dialogError$ = this.dialogErrorSource.asObservable(); - - constructor( - private activeModal: NgbActiveModal, - private solutionEntryService: ProgrammingExerciseSolutionEntryService, - ) {} - - ngOnDestroy(): void { - this.dialogErrorSource.unsubscribe(); - } - - clear() { - this.activeModal.close(); - } - - saveSolutionEntry() { - // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain - this.solutionEntryService.updateSolutionEntry(this.exerciseId, this.solutionEntry.testCase?.id!, this.solutionEntry.id!, this.solutionEntry).subscribe({ - next: (updatedEntry) => { - this.solutionEntry = updatedEntry; - this.activeModal.close(); - }, - error: (error) => this.dialogErrorSource.error(error.message), - }); - } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/code-hint-generation-step/code-hint-generation-step.component.html b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/code-hint-generation-step/code-hint-generation-step.component.html deleted file mode 100644 index 70538fa15b02..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/code-hint-generation-step/code-hint-generation-step.component.html +++ /dev/null @@ -1,74 +0,0 @@ -@if (isLoading) { -
- -
-} @else { -
- @if (codeHints?.length) { - - - } @else { - - } -
- @if (codeHints?.length) { - - - - {{ 'artemisApp.codeHint.management.step4.title' | artemisTranslate }} - {{ 'artemisApp.codeHint.management.step4.task' | artemisTranslate }} - - - @for (codeHint of codeHints; track codeHint) { - - - - {{ codeHint.title }} - - - {{ codeHint?.programmingExerciseTask?.taskName }} - - -
- @if (codeHint?.solutionEntries) { -
- -
- } - -
-
- } -
- } @else { -
- -
- } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/code-hint-generation-step/code-hint-generation-step.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/code-hint-generation-step/code-hint-generation-step.component.ts deleted file mode 100644 index 59e8150e2fe8..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/code-hint-generation-step/code-hint-generation-step.component.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { CodeHint } from 'app/entities/hestia/code-hint-model'; -import { faWrench } from '@fortawesome/free-solid-svg-icons'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { CodeHintService } from 'app/exercises/shared/exercise-hint/services/code-hint.service'; -import { AlertService } from 'app/core/util/alert.service'; - -@Component({ - selector: 'jhi-code-hint-generation-step', - templateUrl: './code-hint-generation-step.component.html', - styleUrls: ['../../code-hint-generation-overview/code-hint-generation-overview.component.scss'], -}) -export class CodeHintGenerationStepComponent implements OnInit { - @Input() - exercise: ProgrammingExercise; - - @Output() - onCodeHintsLoaded = new EventEmitter(); - - isLoading = false; - codeHints?: CodeHint[]; - - faWrench = faWrench; - - constructor( - private exerciseService: ProgrammingExerciseService, - private codeHintService: CodeHintService, - private alertService: AlertService, - ) {} - - ngOnInit() { - this.isLoading = true; - this.exerciseService.getCodeHintsForExercise(this.exercise.id!).subscribe({ - next: (codeHints) => { - this.codeHints = codeHints; - this.onCodeHintsLoaded.emit(this.codeHints); - this.isLoading = false; - }, - error: (error) => { - this.isLoading = false; - this.alertService.error(error.message); - }, - }); - } - - generateCodeHints(deleteOldHints: boolean, buttonTranslationKey: string) { - this.isLoading = true; - // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain - this.codeHintService.generateCodeHintsForExercise(this.exercise?.id!, deleteOldHints).subscribe({ - next: (generatedHints) => { - if (deleteOldHints) { - this.codeHints = generatedHints; - } else { - generatedHints.forEach((generatedHint) => this.codeHints?.push(generatedHint)); - } - this.onCodeHintsLoaded.emit(this.codeHints); - this.isLoading = false; - this.alertService.success('artemisApp.codeHint.management.step4.' + buttonTranslationKey + '.success'); - }, - error: (error) => { - this.isLoading = false; - this.alertService.error(error.message); - }, - }); - } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/coverage-generation-step/coverage-generation-step.component.html b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/coverage-generation-step/coverage-generation-step.component.html deleted file mode 100644 index 421eb8140c86..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/coverage-generation-step/coverage-generation-step.component.html +++ /dev/null @@ -1,13 +0,0 @@ -@if (isLoading) { -
- -
-} -@if (coverageReport && fileContentByPath) { - -} -@if (!(coverageReport || fileContentByPath)) { -
- -
-} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/coverage-generation-step/coverage-generation-step.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/coverage-generation-step/coverage-generation-step.component.ts deleted file mode 100644 index 0fdc47306615..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/coverage-generation-step/coverage-generation-step.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { CoverageReport } from 'app/entities/hestia/coverage-report.model'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { AlertService } from 'app/core/util/alert.service'; - -@Component({ - selector: 'jhi-coverage-generation-step', - templateUrl: './coverage-generation-step.component.html', - styleUrls: ['../../code-hint-generation-overview/code-hint-generation-overview.component.scss'], -}) -export class CoverageGenerationStepComponent implements OnInit { - @Input() - exercise: ProgrammingExercise; - - @Output() - onCoverageLoaded = new EventEmitter(); - - isLoading = false; - coverageReport?: CoverageReport; - fileContentByPath = new Map(); - - constructor( - private exerciseService: ProgrammingExerciseService, - private alertService: AlertService, - ) {} - - ngOnInit(): void { - this.isLoading = true; - this.exerciseService.getSolutionRepositoryTestFilesWithContent(this.exercise.id!).subscribe({ - next: (filesWithContent: Map) => { - this.exerciseService.getLatestFullTestwiseCoverageReport(this.exercise.id!).subscribe({ - next: (coverageReport) => { - this.isLoading = false; - this.onCoverageLoaded.emit(coverageReport); - this.coverageReport = coverageReport; - this.fileContentByPath = filesWithContent; - }, - error: (error) => { - this.isLoading = false; - this.alertService.error(error.message); - }, - }); - }, - }); - } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/diff-generation-step/diff-generation-step.component.html b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/diff-generation-step/diff-generation-step.component.html deleted file mode 100644 index af31d173e0da..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/diff-generation-step/diff-generation-step.component.html +++ /dev/null @@ -1,15 +0,0 @@ -@if (isLoading) { -
- -
-} -@if (!isLoading && gitDiffReport) { -
- -
-} -@if (!isLoading && !gitDiffReport) { -
- -
-} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/diff-generation-step/diff-generation-step.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/diff-generation-step/diff-generation-step.component.ts deleted file mode 100644 index 3903491a1477..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/diff-generation-step/diff-generation-step.component.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { AlertService } from 'app/core/util/alert.service'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; - -@Component({ - selector: 'jhi-diff-generation-step', - templateUrl: './diff-generation-step.component.html', - styleUrls: ['../../code-hint-generation-overview/code-hint-generation-overview.component.scss'], -}) -export class DiffGenerationStepComponent implements OnInit { - @Input() - exercise: ProgrammingExercise; - - @Output() - onGitDiffLoaded = new EventEmitter(); - - isLoading = false; - gitDiffReport?: ProgrammingExerciseGitDiffReport; - templateFileContentByPath: Map; - solutionFileContentByPath: Map; - - constructor( - private exerciseService: ProgrammingExerciseService, - private alertService: AlertService, - ) {} - - ngOnInit() { - this.isLoading = true; - this.exerciseService.getDiffReport(this.exercise.id!).subscribe({ - next: (report) => { - this.gitDiffReport = report; - this.onGitDiffLoaded.emit(report); - this.exerciseService.getTemplateRepositoryTestFilesWithContent(this.exercise.id!).subscribe({ - next: (response: Map) => { - this.templateFileContentByPath = response; - this.isLoading = this.solutionFileContentByPath === undefined; - }, - }); - this.exerciseService.getSolutionRepositoryTestFilesWithContent(this.exercise.id!).subscribe({ - next: (response: Map) => { - this.solutionFileContentByPath = response; - this.isLoading = this.templateFileContentByPath === undefined; - }, - }); - }, - error: (error) => { - this.isLoading = false; - this.alertService.error(error.message); - }, - }); - } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/solution-entry-generation-step/solution-entry-generation-step.component.html b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/solution-entry-generation-step/solution-entry-generation-step.component.html deleted file mode 100644 index 3e3b36f53456..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/solution-entry-generation-step/solution-entry-generation-step.component.html +++ /dev/null @@ -1,97 +0,0 @@ -@if (isLoading) { -
- -
-} -
- - - @if (!!exercise?.buildConfig?.testwiseCoverageEnabled) { - - } - -
-
- - - - - - - - - - - - @for (entry of solutionEntries; track entry) { - - - - - - - - - } - -
- {{ 'artemisApp.programmingExerciseTestCase.testName' | artemisTranslate }} - - File Path - - -
{{ entry?.id }}{{ entry?.testCase?.testName }}{{ entry?.filePath }} - {{ - (entry.code?.split('\n')?.length! === 1 ? 'artemisApp.codeHint.lineOfCode' : 'artemisApp.codeHint.linesOfCode') - | artemisTranslate: { lines: entry.code?.split('\n')?.length! } - }} - {{ 'artemisApp.codeHint.entryAbbreviation.' + entry.testCase?.type?.toString()?.toLocaleLowerCase() | artemisTranslate }} -
- - - -
-
- @if (!solutionEntries?.length) { -
- -
- } -
diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/solution-entry-generation-step/solution-entry-generation-step.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/solution-entry-generation-step/solution-entry-generation-step.component.ts deleted file mode 100644 index 5d4b3c68f675..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/solution-entry-generation-step/solution-entry-generation-step.component.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { SolutionEntryDetailsModalComponent } from 'app/exercises/programming/hestia/generation-overview/solution-entry-details-modal/solution-entry-details-modal.component'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExerciseTestCaseType } from 'app/entities/programming/programming-exercise-test-case.model'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { AlertService } from 'app/core/util/alert.service'; -import { Subject } from 'rxjs'; -import { faSort, faSortDown, faSortUp, faTimes } from '@fortawesome/free-solid-svg-icons'; -import { CodeHintService } from 'app/exercises/shared/exercise-hint/services/code-hint.service'; -import { ManualSolutionEntryCreationModalComponent } from 'app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component'; -import { SortingOrder } from 'app/shared/table/pageable-table'; -import { ProgrammingExerciseSolutionEntryService } from 'app/exercises/shared/exercise-hint/services/programming-exercise-solution-entry.service'; -import { ConfirmAutofocusModalComponent } from 'app/shared/components/confirm-autofocus-modal.component'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; - -@Component({ - selector: 'jhi-solution-entry-generation-step', - templateUrl: './solution-entry-generation-step.component.html', - styleUrls: ['../../code-hint-generation-overview/code-hint-generation-overview.component.scss'], -}) -export class SolutionEntryGenerationStepComponent implements OnInit, OnDestroy { - @Input() - exercise: ProgrammingExercise; - - @Output() - onEntryUpdate = new EventEmitter(); - - isLoading: boolean; - solutionEntries: ProgrammingExerciseSolutionEntry[]; - faTimes = faTimes; - - testCaseSortOrder?: SortingOrder; - faSort = faSort; - faSortUp = faSortUp; - faSortDown = faSortDown; - - private dialogErrorSource = new Subject(); - dialogError$ = this.dialogErrorSource.asObservable(); - - readonly SortingOrder = SortingOrder; - - constructor( - private modalService: NgbModal, - private exerciseService: ProgrammingExerciseService, - private alertService: AlertService, - private artemisTranslatePipe: ArtemisTranslatePipe, - private codeHintService: CodeHintService, - private solutionEntryService: ProgrammingExerciseSolutionEntryService, - ) {} - - ngOnInit() { - this.isLoading = true; - this.solutionEntryService.getSolutionEntriesForExercise(this.exercise.id!).subscribe({ - next: (solutionEntries: ProgrammingExerciseSolutionEntry[]) => { - this.solutionEntries = solutionEntries; - this.isLoading = false; - this.onEntryUpdate.emit(this.solutionEntries); - }, - error: (error) => { - this.isLoading = false; - this.alertService.error(error.message); - }, - }); - } - - ngOnDestroy(): void { - this.dialogErrorSource.unsubscribe(); - } - - openSolutionEntryModal(solutionEntry: ProgrammingExerciseSolutionEntry, isEditable: boolean) { - const modalRef: NgbModalRef = this.modalService.open(SolutionEntryDetailsModalComponent as Component, { - size: 'lg', - backdrop: 'static', - }); - modalRef.componentInstance.exerciseId = this.exercise.id; - modalRef.componentInstance.solutionEntry = solutionEntry; - modalRef.componentInstance.isEditable = isEditable; - } - - openManualEntryCreationModal() { - const modalRef: NgbModalRef = this.modalService.open(ManualSolutionEntryCreationModalComponent as Component, { - size: 'lg', - backdrop: 'static', - }); - modalRef.componentInstance.exerciseId = this.exercise.id; - modalRef.componentInstance.onEntryCreated.subscribe((createdEntry: ProgrammingExerciseSolutionEntry) => { - this.solutionEntries.push(createdEntry); - this.onEntryUpdate.emit(this.solutionEntries); - }); - } - - onGenerateStructuralSolutionEntries() { - this.exerciseService.createStructuralSolutionEntries(this.exercise.id!).subscribe({ - next: (updatedStructuralEntries) => { - this.alertService.success('artemisApp.programmingExercise.createStructuralSolutionEntriesSuccess'); - // replace all structural entries - const result = this.removeSolutionEntriesOfTypeFromArray(this.solutionEntries, ProgrammingExerciseTestCaseType.STRUCTURAL); - Array.prototype.push.apply(result, updatedStructuralEntries); - this.solutionEntries = result; - this.testCaseSortOrder = undefined; - this.onEntryUpdate.emit(this.solutionEntries); - }, - error: (error) => this.alertService.error(error.message), - }); - } - - onGenerateBehavioralSolutionEntries() { - this.exerciseService.createBehavioralSolutionEntries(this.exercise.id!).subscribe({ - next: (updatedBehavioralEntries) => { - this.alertService.success('artemisApp.programmingExercise.createBehavioralSolutionEntriesSuccess'); - // replace all behavioral entries - const result = this.removeSolutionEntriesOfTypeFromArray(this.solutionEntries, ProgrammingExerciseTestCaseType.BEHAVIORAL); - Array.prototype.push.apply(result, updatedBehavioralEntries); - this.solutionEntries = result; - this.testCaseSortOrder = undefined; - this.onEntryUpdate.emit(this.solutionEntries); - }, - error: (error) => this.alertService.error(error.message), - }); - } - - openBulkDeletionModal() { - const modalRef = this.modalService.open(ConfirmAutofocusModalComponent, { keyboard: true, size: 'lg' }); - modalRef.componentInstance.title = 'artemisApp.codeHint.management.step3.deleteAllEntriesButton.title'; - modalRef.componentInstance.text = this.artemisTranslatePipe.transform('artemisApp.codeHint.management.step3.deleteAllEntriesButton.question'); - modalRef.result.then(() => { - this.deleteAllSolutionEntries(); - }); - } - - deleteAllSolutionEntries() { - // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain - this.solutionEntryService.deleteAllSolutionEntriesForExercise(this.exercise?.id!).subscribe({ - next: () => { - this.solutionEntries = []; - this.onEntryUpdate.emit([]); - this.alertService.success('artemisApp.codeHint.management.step3.deleteAllEntriesButton.success'); - this.dialogErrorSource.next(''); - }, - error: (error) => this.dialogErrorSource.error(error.message), - }); - } - - deleteSolutionEntry(entry: ProgrammingExerciseSolutionEntry) { - // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain - this.solutionEntryService.deleteSolutionEntry(this.exercise?.id!, entry.testCase?.id!, entry.id!).subscribe({ - next: () => { - this.solutionEntries = this.solutionEntries.filter((existingEntry) => entry !== existingEntry); - this.dialogErrorSource.next(''); - this.onEntryUpdate.emit(this.solutionEntries); - }, - error: (error) => this.dialogErrorSource.error(error.message), - }); - } - - changeTestCaseSortOrder() { - switch (this.testCaseSortOrder) { - case SortingOrder.ASCENDING: - this.solutionEntries = this.solutionEntries.reverse(); - this.testCaseSortOrder = SortingOrder.DESCENDING; - break; - case SortingOrder.DESCENDING: - this.solutionEntries = this.solutionEntries.reverse(); - this.testCaseSortOrder = SortingOrder.ASCENDING; - break; - case undefined: - // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain - this.solutionEntries = this.solutionEntries.sort((a, b) => a.testCase?.testName!.localeCompare(b.testCase?.testName!)!); - this.testCaseSortOrder = SortingOrder.ASCENDING; - } - } - - private removeSolutionEntriesOfTypeFromArray(entries: ProgrammingExerciseSolutionEntry[], typeToRemove: ProgrammingExerciseTestCaseType) { - return entries.filter((entry) => entry.testCase?.type !== typeToRemove); - } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-file.component.html b/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-file.component.html deleted file mode 100644 index 3690bfdf0e3b..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-file.component.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - {{ fileName }} - - {{ proportionString }} - -
- -
-
diff --git a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-file.component.scss b/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-file.component.scss deleted file mode 100644 index 915e18b1199e..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-file.component.scss +++ /dev/null @@ -1,20 +0,0 @@ -.mat-expansion-panel-header { - padding: 0; -} - -.mat-expansion-panel .mat-expansion-panel-body { - padding: 0 !important; -} - -.mat-expansion-panel-header-description { - flex-grow: 0; -} - -.mat-expansion-panel .mat-expansion-indicator::after { - margin-right: 2px !important; - margin-left: 2px !important; -} - -.covered-line-highlight { - background-color: var(--monaco-editor-test-coverage-highlight); -} diff --git a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-file.component.ts b/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-file.component.ts deleted file mode 100644 index b9fc28de4fbf..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-file.component.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core'; -import { CoverageFileReport } from 'app/entities/hestia/coverage-file-report.model'; -import { MonacoEditorComponent } from 'app/shared/monaco-editor/monaco-editor.component'; - -@Component({ - selector: 'jhi-testwise-coverage-file', - templateUrl: './testwise-coverage-file.component.html', - styleUrls: ['./testwise-coverage-file.component.scss'], - encapsulation: ViewEncapsulation.None, -}) -export class TestwiseCoverageFileComponent implements OnInit, OnChanges { - @Input() - fileContent: string; - - @Input() - fileName: string; - - @Input() - fileReport: CoverageFileReport; - - @ViewChild('editor', { static: true }) - editor: MonacoEditorComponent; - - proportionCoveredLines: number; - proportionString: string; - editorHeight: number = 20; - - static readonly COVERED_LINE_HIGHLIGHT_CLASS = 'covered-line-highlight'; - - ngOnInit(): void { - this.renderFile(); - this.editorHeight = this.editor.getContentHeight(); - } - - ngOnChanges(changes: SimpleChanges): void { - if (changes.fileReport || changes.fileContent) { - this.renderFile(); - } - } - - private aggregateCoveredLinesBlocks(fileReport: CoverageFileReport): Map { - const coveredLines = new Set(); - const entries = fileReport.testwiseCoverageEntries!; - // retrieve all covered line numbers - entries.forEach((entry) => { - this.getRangeArray(entry.startLine!, entry.lineCount!).forEach((line) => coveredLines.add(line)); - }); - - // build the blocks - const orderedLines = Array.from(coveredLines).sort(); - const startLineByLength = new Map(); - - // set the covered line ratio accordingly - this.proportionCoveredLines = orderedLines.length / this.fileReport!.lineCount!; - this.proportionString = `${(this.proportionCoveredLines * 100).toFixed(1)} %`; - - let index = 0; - while (index < orderedLines.length) { - const currentBlockStartLine = orderedLines[index]; - let currentBlockLength = 1; - let continueBlock = true; - - // count the length of the consecutive blocks - while (continueBlock && index < orderedLines.length) { - if (index + 1 === orderedLines.length) { - continueBlock = false; - } else if (orderedLines[index + 1] === orderedLines[index] + 1) { - currentBlockLength++; - } else { - continueBlock = false; - } - index++; - } - startLineByLength.set(currentBlockStartLine, currentBlockLength); - } - - return startLineByLength; - } - - private getRangeArray(startLine: number, lineCount: number): number[] { - return [...Array(lineCount).keys()].map((i) => i + startLine - 1); - } - - private renderFile() { - this.editor.changeModel(this.fileName, this.fileContent ?? ''); - this.editor.disposeLineHighlights(); - this.aggregateCoveredLinesBlocks(this.fileReport).forEach((blockLength, lineNumber) => { - this.editor.highlightLines(lineNumber + 1, lineNumber + blockLength, 'covered-line-highlight', 'covered-line-highlight'); - }); - } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report-modal.component.html b/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report-modal.component.html deleted file mode 100644 index 01e96e404923..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report-modal.component.html +++ /dev/null @@ -1,11 +0,0 @@ -
- - -
diff --git a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report-modal.component.ts b/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report-modal.component.ts deleted file mode 100644 index 2183198c8772..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report-modal.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { CoverageReport } from 'app/entities/hestia/coverage-report.model'; - -@Component({ - selector: 'jhi-testwise-coverage-report-modal', - templateUrl: './testwise-coverage-report-modal.component.html', -}) -export class TestwiseCoverageReportModalComponent { - @Input() - report: CoverageReport; - - @Input() - fileContentByPath: Map; - - constructor(protected activeModal: NgbActiveModal) {} - - close(): void { - this.activeModal.dismiss(); - } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.component.html b/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.component.html deleted file mode 100644 index b16a1eccd579..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.component.html +++ /dev/null @@ -1,17 +0,0 @@ - -@for (entry of displayedTestCaseNames | keyvalue; track entry) { -
- - {{ entry.key }} -
-} -
-
-
-
-
-@for (entry of fileReportByFileName | keyvalue; track identifyCoverageFileComponent($index, entry)) { -
- -
-} diff --git a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.component.ts b/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.component.ts deleted file mode 100644 index a5f6b75f05a7..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.component.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Component, Input, OnInit } from '@angular/core'; -import { CoverageReport } from 'app/entities/hestia/coverage-report.model'; -import { CoverageFileReport } from 'app/entities/hestia/coverage-file-report.model'; - -@Component({ - selector: 'jhi-testwise-coverage-report', - templateUrl: './testwise-coverage-report.component.html', -}) -export class TestwiseCoverageReportComponent implements OnInit { - @Input() - report: CoverageReport; - - @Input() - fileContentByPath: Map; - - displayedTestCaseNames: Map; - - fileReportByFileName: Map; - - constructor() {} - - ngOnInit(): void { - // initially display covered lines for all test cases - this.displayedTestCaseNames = new Map(); - // retrieve all test cases - const testCases = new Set( - this.report?.fileReports - ?.flatMap((report) => report) - .flatMap((fileReport) => fileReport.testwiseCoverageEntries) - .map((entry) => entry!.testCase), - ); - testCases.forEach((testCase) => this.displayedTestCaseNames.set(testCase!.testName!, true)); - - this.setReportsByFileName(); - } - - changeReportsBySelectedTestCases(testCaseName: string): void { - const selected = this.displayedTestCaseNames.get(testCaseName); - this.displayedTestCaseNames.set(testCaseName, !selected); - this.setReportsByFileName(); - } - - private setReportsByFileName(): void { - const result = new Map(); - // create the reports for all files, not only for files that have existing coverage data - this.fileContentByPath?.forEach((content, filePath) => { - // do not include non-java/kotlin files - if (!(filePath.endsWith('.java') || filePath.endsWith('.kt'))) { - return; - } - const matchingFileReport = this.report.fileReports?.filter((fileReport) => fileReport?.filePath === filePath)?.first(); - const copiedFileReport = new CoverageFileReport(); - copiedFileReport.filePath = filePath; - if (matchingFileReport) { - copiedFileReport.lineCount = matchingFileReport.lineCount; - copiedFileReport.coveredLineCount = matchingFileReport.coveredLineCount; - - // filter out entries for the current file report - copiedFileReport.testwiseCoverageEntries = matchingFileReport.testwiseCoverageEntries?.filter((entry) => - this.displayedTestCaseNames.get(entry.testCase!.testName!), - ); - } else { - copiedFileReport.lineCount = content.split('\n').length; - copiedFileReport.coveredLineCount = 0; - copiedFileReport.testwiseCoverageEntries = []; - } - result.set(filePath, copiedFileReport); - }); - this.fileReportByFileName = result; - } - - identifyCoverageFileComponent(index: number, item: any) { - return item.key; - } -} diff --git a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.module.ts b/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.module.ts deleted file mode 100644 index c37bc7a255da..000000000000 --- a/src/main/webapp/app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NgModule } from '@angular/core'; -import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { TestwiseCoverageReportModalComponent } from 'app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report-modal.component'; -import { TestwiseCoverageReportComponent } from 'app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.component'; -import { TestwiseCoverageFileComponent } from 'app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-file.component'; -import { MatExpansionModule } from '@angular/material/expansion'; -import { MonacoEditorComponent } from 'app/shared/monaco-editor/monaco-editor.component'; - -@NgModule({ - imports: [ArtemisSharedModule, MatExpansionModule, MonacoEditorComponent], - declarations: [TestwiseCoverageFileComponent, TestwiseCoverageReportComponent, TestwiseCoverageReportModalComponent], - exports: [TestwiseCoverageFileComponent, TestwiseCoverageReportModalComponent, TestwiseCoverageReportComponent], -}) -export class TestwiseCoverageReportModule {} diff --git a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component.ts index bd491c999df9..ec88a4dc9ae1 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component.ts @@ -8,7 +8,7 @@ import { ProgrammingExerciseTask } from './programming-exercise-task'; import { Observable, Subject } from 'rxjs'; import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { isExamExercise } from 'app/shared/util/utils'; -import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; +import { ProgrammingExerciseServerSideTask } from 'app/entities/programming-exercise-task.model'; type Sort = { by: 'name' | 'weight' | 'multiplier' | 'bonusPoints' | 'visibility' | 'resulting' | 'type'; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.service.ts b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.service.ts index 5f45d551b476..3021e5e548f2 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.service.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.service.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; +import { ProgrammingExerciseServerSideTask } from 'app/entities/programming-exercise-task.model'; import { Observable, catchError, of, tap } from 'rxjs'; import { Exercise } from 'app/entities/exercise.model'; import { ProgrammingExerciseTask } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task'; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.ts b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.ts index 3a5844ddbb9e..101498cc8cf4 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.ts @@ -1,4 +1,4 @@ -import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; +import { ProgrammingExerciseServerSideTask } from 'app/entities/programming-exercise-task.model'; import { TestCaseStats } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; import { ProgrammingExerciseTestCase, ProgrammingExerciseTestCaseType, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.html b/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.html index 95da3dea7a71..d3809ff3fdc8 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.html +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.html @@ -40,15 +40,6 @@

{{ } - @if (programmingExercise.course && !isExamExercise) { - - - - - } @@ -134,27 +125,6 @@

{{ } - @if (programmingExercise.isAtLeastEditor) { - - @if ( - (programmingExercise.programmingLanguage === ProgrammingLanguage.JAVA || - programmingExercise.programmingLanguage === ProgrammingLanguage.KOTLIN) && - programmingExercise.buildConfig?.testwiseCoverageEnabled - ) { - - - - } - - } @if (programmingExercise.isAtLeastInstructor && !localVCEnabled) { - - } - @if (exercise?.id) { -
- - -
- } - -
- - -
- - - diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint-update.component.ts b/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint-update.component.ts deleted file mode 100644 index 260aba7f73e9..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint-update.component.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { HttpResponse } from '@angular/common/http'; -import { ActivatedRoute } from '@angular/router'; -import { Observable, Subscription, filter, switchMap } from 'rxjs'; -import { AlertService } from 'app/core/util/alert.service'; -import { ExerciseHintService } from '../shared/exercise-hint.service'; -import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; -import { faBan, faCircleNotch, faSave } from '@fortawesome/free-solid-svg-icons'; -import { ExerciseHint, HintType } from 'app/entities/hestia/exercise-hint.model'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; -import { ManualSolutionEntryCreationModalComponent } from 'app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { CodeHint } from 'app/entities/hestia/code-hint-model'; -import { CodeHintService } from 'app/exercises/shared/exercise-hint/services/code-hint.service'; -import { onError } from 'app/shared/util/global.utils'; -import { IrisSettingsService } from 'app/iris/settings/shared/iris-settings.service'; -import { IrisSettings } from 'app/entities/iris/settings/iris-settings.model'; -import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; -import { ButtonType } from 'app/shared/components/button.component'; -import { PROFILE_IRIS } from 'app/app.constants'; -import { FormulaAction } from 'app/shared/monaco-editor/model/actions/formula.action'; -import { MarkdownEditorHeight } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; - -const DEFAULT_DISPLAY_THRESHOLD = 3; - -@Component({ - selector: 'jhi-exercise-hint-update', - templateUrl: './exercise-hint-update.component.html', -}) -export class ExerciseHintUpdateComponent implements OnInit, OnDestroy { - MarkdownEditorHeight = MarkdownEditorHeight; - - courseId: number; - exercise: ProgrammingExercise; - exerciseHint = new ExerciseHint(); - solutionEntries: ProgrammingExerciseSolutionEntry[]; - - programmingExercise: ProgrammingExercise; - tasks: ProgrammingExerciseServerSideTask[]; - irisSettings?: IrisSettings; - - isSaving: boolean; - isGeneratingDescription: boolean; - paramSub: Subscription; - - domainActions = [new FormulaAction()]; - - // Icons - faCircleNotch = faCircleNotch; - faBan = faBan; - faSave = faSave; - - // Enums - readonly HintType = HintType; - readonly ButtonType = ButtonType; - - constructor( - private route: ActivatedRoute, - protected alertService: AlertService, - private modalService: NgbModal, - protected codeHintService: CodeHintService, - protected exerciseHintService: ExerciseHintService, - private programmingExerciseService: ProgrammingExerciseService, - private navigationUtilService: ArtemisNavigationUtilService, - protected irisSettingsService: IrisSettingsService, - private profileService: ProfileService, - ) {} - - /** - * Fetches the exercise from the server and assigns it on the exercise hint - */ - ngOnInit() { - this.paramSub = this.route.params.subscribe((params) => { - this.courseId = params['courseId']; - this.isSaving = false; - this.isGeneratingDescription = false; - }); - this.route.data.subscribe(({ exerciseHint, exercise }) => { - this.exercise = exercise; - exerciseHint.exercise = exercise; - this.exerciseHint = exerciseHint; - this.exerciseHint.displayThreshold = this.exerciseHint.displayThreshold ?? DEFAULT_DISPLAY_THRESHOLD; - - this.programmingExerciseService.getTasksAndTestsExtractedFromProblemStatement(this.exercise.id!).subscribe((tasks) => { - this.tasks = tasks; - - const selectedTask = this.tasks.find((task) => task.id === this.exerciseHint.programmingExerciseTask?.id); - if (selectedTask) { - this.exerciseHint.programmingExerciseTask = selectedTask; - } else if (tasks.length) { - this.exerciseHint.programmingExerciseTask = this.tasks[0]; - } - }); - - this.profileService - .getProfileInfo() - .pipe( - filter((profileInfo) => profileInfo?.activeProfiles?.includes(PROFILE_IRIS)), - switchMap(() => this.irisSettingsService.getCombinedExerciseSettings(this.exercise.id!)), - ) - .subscribe((settings) => { - this.irisSettings = settings; - }); - }); - } - - openManualEntryCreationModal() { - const codeHint = this.exerciseHint as CodeHint; - const testCasesForCurrentTask = codeHint.programmingExerciseTask?.testCases ?? []; - const modalRef: NgbModalRef = this.modalService.open(ManualSolutionEntryCreationModalComponent as Component, { size: 'lg', backdrop: 'static' }); - modalRef.componentInstance.exerciseId = this.exercise.id!; - modalRef.componentInstance.codeHint = codeHint; - modalRef.componentInstance.testCases = testCasesForCurrentTask; - modalRef.componentInstance.onEntryCreated.subscribe((createdEntry: ProgrammingExerciseSolutionEntry) => { - codeHint!.solutionEntries!.push(createdEntry); - }); - } - - /** - * Unsubscribes from the param subscription - */ - ngOnDestroy(): void { - if (this.paramSub) { - this.paramSub.unsubscribe(); - } - } - - /** - * Setter to update the exercise hint content - * @param newContent New value to set - */ - updateHintContent(newContent: string) { - this.exerciseHint.content = newContent; - } - - /** - * Navigate to the previous page when the user cancels the update process - * Returns to the detail page if there is no previous state and we edited an existing hint - * Returns to the overview page if there is no previous state and we created a new hint - */ - previousState() { - this.navigationUtilService.navigateBackWithOptional( - ['course-management', this.courseId.toString(), 'programming-exercises', this.exercise.id!.toString(), 'hints'], - this.exerciseHint.id?.toString(), - ); - } - - /** - * Saves the exercise hint by creating or updating it on the server - */ - save() { - this.isSaving = true; - if (this.exerciseHint.id !== undefined) { - this.subscribeToSaveResponse(this.exerciseHintService.update(this.exercise.id!, this.exerciseHint)); - } else { - this.subscribeToSaveResponse(this.exerciseHintService.create(this.exercise.id!, this.exerciseHint)); - } - } - - generateDescriptionForCodeHint() { - if (((this.exerciseHint as CodeHint).solutionEntries?.length ?? 0) === 0) { - return; - } - this.isGeneratingDescription = true; - this.codeHintService.generateDescriptionForCodeHint(this.exercise.id!, this.exerciseHint.id!).subscribe({ - next: (response) => { - this.exerciseHint.description = response.body!.description; - this.exerciseHint.content = response.body!.content; - this.isGeneratingDescription = false; - }, - error: (error) => { - this.isGeneratingDescription = false; - onError(this.alertService, error); - }, - }); - } - - protected subscribeToSaveResponse(result: Observable>) { - result.subscribe({ - next: () => this.onSaveSuccess(), - error: () => this.onSaveError(), - }); - } - - protected onSaveSuccess() { - this.isSaving = false; - this.previousState(); - } - - protected onSaveError() { - this.isSaving = false; - } -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.component.html b/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.component.html deleted file mode 100644 index 59db6e86ff2f..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.component.html +++ /dev/null @@ -1,110 +0,0 @@ -
-

- - @if (exercise?.programmingLanguage === ProgrammingLanguage.JAVA) { - - @if (containsCodeHints) { -
- - Manage Code Hints -
- } - @if (!containsCodeHints) { -
- - -
- } -
- } - - - - -

-
- @if (exerciseHints?.length === 0) { -
- -
- } - @if (exerciseHints && exerciseHints.length > 0) { -
- - - - - - - - - - - - @for (exerciseHint of exerciseHints; track trackId($index, exerciseHint)) { - - - - - - - - } - -
- {{ exerciseHint.id }} - - {{ exerciseHint.title }} -
- @if (exerciseHint.type === HintType.CODE && exerciseHint.exercise?.type === ExerciseType.PROGRAMMING) { - {{ 'artemisApp.codeHint.type' | artemisTranslate }} - } - @if (exerciseHint.type === HintType.TEXT && exerciseHint.exercise?.type === ExerciseType.PROGRAMMING) { - {{ 'artemisApp.exerciseHint.textHint' | artemisTranslate }} - } -
-
- -
- @if (exerciseHint.type === HintType.CODE) { -
- - {{ (exerciseHint | castToCodeHint).solutionEntries?.length ?? 0 }} - {{ - ((exerciseHint | castToCodeHint).solutionEntries?.length === 1 ? 'artemisApp.codeHint.entry' : 'artemisApp.codeHint.entries') - | artemisTranslate - }} - -
- } -
- {{ exerciseHint.exercise?.id }} - -
- - - - - - - - - -
-
-
- } -
diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.component.ts b/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.component.ts deleted file mode 100644 index 3b542f17643b..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.component.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { Subject, Subscription } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; - -import { ExerciseHintService } from '../shared/exercise-hint.service'; -import { onError } from 'app/shared/util/global.utils'; -import { AlertService } from 'app/core/util/alert.service'; -import { EventManager } from 'app/core/util/event-manager.service'; -import { faArrowsRotate, faCode, faEye, faFont, faPlus, faTimes, faWrench } from '@fortawesome/free-solid-svg-icons'; -import { ExerciseHint, HintType } from 'app/entities/hestia/exercise-hint.model'; -import { ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; - -@Component({ - selector: 'jhi-exercise-hint', - templateUrl: './exercise-hint.component.html', -}) -export class ExerciseHintComponent implements OnInit, OnDestroy { - readonly HintType = HintType; - ExerciseType = ExerciseType; - exercise: ProgrammingExercise; - exerciseHints: ExerciseHint[]; - containsCodeHints: boolean; - eventSubscriber: Subscription; - - private dialogErrorSource = new Subject(); - dialogError$ = this.dialogErrorSource.asObservable(); - - paramSub: Subscription; - - // Icons - faPlus = faPlus; - faTimes = faTimes; - faEye = faEye; - faWrench = faWrench; - faText = faFont; - faCode = faCode; - faArrowsRotate = faArrowsRotate; - - readonly ProgrammingLanguage = ProgrammingLanguage; - - constructor( - private route: ActivatedRoute, - protected exerciseHintService: ExerciseHintService, - private alertService: AlertService, - protected eventManager: EventManager, - ) {} - - /** - * Subscribes to the route params to act on the currently selected exercise. - */ - ngOnInit() { - this.route.data.subscribe(({ exercise }) => { - this.exercise = exercise; - this.loadAllByExerciseId(); - this.registerChangeInExerciseHints(); - }); - } - - /** - * Unsubscribe from subscriptions - */ - ngOnDestroy() { - if (this.paramSub) { - this.paramSub.unsubscribe(); - } - this.eventManager.destroy(this.eventSubscriber); - this.dialogErrorSource.unsubscribe(); - } - - /** - * Load all exercise hints with the currently selected exerciseId (taken from route params). - */ - loadAllByExerciseId() { - this.exerciseHintService - .findByExerciseId(this.exercise.id!) - .pipe( - filter((res: HttpResponse) => res.ok), - map((res: HttpResponse) => res.body), - ) - .subscribe({ - next: (res: ExerciseHint[]) => { - this.exerciseHints = res; - this.containsCodeHints = this.exerciseHints?.some((hint) => hint.type === HintType.CODE); - }, - error: (res: HttpErrorResponse) => onError(this.alertService, res), - }); - } - - /** - * Returns the track id of an exercise hint - * @param index Index of the item - * @param item Item for which to get the id - */ - trackId(index: number, item: ExerciseHint) { - return item.id; - } - - /** - * (Re-)subscribe to the exercise hint list modification subscription - */ - registerChangeInExerciseHints() { - if (this.eventSubscriber) { - this.eventSubscriber.unsubscribe(); - } - this.eventSubscriber = this.eventManager.subscribe('exerciseHintListModification', () => this.loadAllByExerciseId()); - } - - /** - * Deletes exercise hint - * @param exerciseHintId the id of the exercise hint that we want to delete - */ - deleteExerciseHint(exerciseHintId: number) { - this.exerciseHintService.delete(this.exercise.id!, exerciseHintId).subscribe({ - next: () => { - this.eventManager.broadcast({ - name: 'exerciseHintListModification', - content: 'Deleted an exerciseHint', - }); - this.dialogErrorSource.next(''); - }, - error: (error: HttpErrorResponse) => this.dialogErrorSource.next(error.message), - }); - } -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.route.ts b/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.route.ts deleted file mode 100644 index dd64cf79d51e..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.route.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpResponse } from '@angular/common/http'; -import { ActivatedRouteSnapshot, Resolve, Routes } from '@angular/router'; -import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { of } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { ExerciseHintService } from '../shared/exercise-hint.service'; -import { ExerciseHintComponent } from './exercise-hint.component'; -import { ExerciseHintDetailComponent } from './exercise-hint-detail.component'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; -import { Authority } from 'app/shared/constants/authority.constants'; -import { exerciseTypes } from 'app/entities/exercise.model'; -import { ExerciseHintUpdateComponent } from 'app/exercises/shared/exercise-hint/manage/exercise-hint-update.component'; -import { ProgrammingExerciseResolve } from 'app/exercises/programming/manage/programming-exercise-management-routing.module'; - -@Injectable({ providedIn: 'root' }) -export class ExerciseHintResolve implements Resolve { - constructor(private service: ExerciseHintService) {} - - /** - * Resolves the route into an exercise hint id and fetches it from the server - * @param route Route which to resolve - */ - resolve(route: ActivatedRouteSnapshot) { - const exerciseId = route.params['exerciseId'] ? route.params['exerciseId'] : undefined; - const hintId = route.params['hintId'] ? route.params['hintId'] : undefined; - if (exerciseId && hintId) { - return this.service.find(exerciseId, hintId).pipe( - filter((response: HttpResponse) => response.ok), - map((exerciseHint: HttpResponse) => exerciseHint.body!), - ); - } - return of(new ExerciseHint()); - } -} - -export const exerciseHintRoute: Routes = [ - ...exerciseTypes.map((exerciseType) => { - return { - path: ':courseId/' + exerciseType + '-exercises/:exerciseId/exercise-hints/new', - component: ExerciseHintUpdateComponent, - resolve: { - exercise: ProgrammingExerciseResolve, - exerciseHint: ExerciseHintResolve, - }, - data: { - authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], - pageTitle: 'artemisApp.exerciseHint.home.title', - }, - canActivate: [UserRouteAccessService], - }; - }), - ...exerciseTypes.map((exerciseType) => { - return { - path: ':courseId/' + exerciseType + '-exercises/:exerciseId/exercise-hints/:hintId', - component: ExerciseHintDetailComponent, - resolve: { - exerciseHint: ExerciseHintResolve, - }, - data: { - authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], - pageTitle: 'artemisApp.exerciseHint.home.title', - }, - canActivate: [UserRouteAccessService], - }; - }), - ...exerciseTypes.map((exerciseType) => { - return { - path: ':courseId/' + exerciseType + '-exercises/:exerciseId/exercise-hints/:hintId/edit', - component: ExerciseHintUpdateComponent, - resolve: { - exercise: ProgrammingExerciseResolve, - exerciseHint: ExerciseHintResolve, - }, - data: { - authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], - pageTitle: 'artemisApp.exerciseHint.home.title', - }, - canActivate: [UserRouteAccessService], - }; - }), - ...exerciseTypes.map((exerciseType) => { - return { - path: ':courseId/' + exerciseType + '-exercises/:exerciseId/exercise-hints', - component: ExerciseHintComponent, - resolve: { - exercise: ProgrammingExerciseResolve, - }, - data: { - authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], - pageTitle: 'artemisApp.exerciseHint.home.title', - }, - canActivate: [UserRouteAccessService], - }; - }), -]; diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component.html b/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component.html deleted file mode 100644 index 01e2077a3ae0..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component.html +++ /dev/null @@ -1,13 +0,0 @@ -
- @if (availableExerciseHints && activatedExerciseHints && availableExerciseHints.length + activatedExerciseHints.length > 0) { - - } -
diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component.scss b/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component.scss deleted file mode 100644 index 98247be1b9c1..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component.scss +++ /dev/null @@ -1,20 +0,0 @@ -:host { - position: sticky; - bottom: 0; - left: 0; - z-index: 100; - overflow: hidden; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.btn { - box-shadow: none; -} - -.animated-button { - animation: 0.4s highlight; - animation-delay: 0.8s; - animation-iteration-count: 3; -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component.ts b/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component.ts deleted file mode 100644 index 07e11494103b..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; -import { faCircleQuestion } from '@fortawesome/free-solid-svg-icons'; -import { ExerciseHintStudentDialogComponent } from 'app/exercises/shared/exercise-hint/participate/exercise-hint-student-dialog.component'; -import cloneDeep from 'lodash-es/cloneDeep'; - -@Component({ - selector: 'jhi-exercise-hint-button-overlay', - templateUrl: './exercise-hint-button-overlay.component.html', - styleUrls: ['./exercise-hint-button-overlay.component.scss'], -}) -export class ExerciseHintButtonOverlayComponent { - @Input() - availableExerciseHints?: ExerciseHint[]; - @Input() - activatedExerciseHints?: ExerciseHint[]; - @Output() - onHintActivated = new EventEmitter(); - - faCircleQuestion = faCircleQuestion; - ngbModalRef?: NgbModalRef; - - constructor(private modalService: NgbModal) {} - - openModal() { - this.ngbModalRef = this.modalService.open(ExerciseHintStudentDialogComponent as Component, { size: 'lg', backdrop: 'static' }); - this.ngbModalRef.componentInstance.onHintActivated = this.onHintActivated; - // cloning is required that the lists not change while modal is open - this.ngbModalRef.componentInstance.activatedExerciseHints = cloneDeep(this.activatedExerciseHints!); - this.ngbModalRef.componentInstance.availableExerciseHints = cloneDeep(this.availableExerciseHints!); - this.ngbModalRef.result.then( - () => { - this.ngbModalRef = undefined; - }, - () => { - this.ngbModalRef = undefined; - }, - ); - } -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-expandable.component.html b/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-expandable.component.html deleted file mode 100644 index 7dc825d8aa95..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-expandable.component.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - -
-
{{ exerciseHint!.description }}
-
{{ exerciseHint!.programmingExerciseTask?.taskName }}
-
-
- - -
-
- - - - @if (isLoading) { -
-
- -
-
- } @else { -
- @if (exerciseHint?.content) { -
- } - @if (exerciseHint.type === HintType.CODE && (exerciseHint | castToCodeHint)?.solutionEntries) { -
- -
- } -
- How helpful was this hint? - -
-
- } -
diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-expandable.component.scss b/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-expandable.component.scss deleted file mode 100644 index 0768618c0d5d..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-expandable.component.scss +++ /dev/null @@ -1,31 +0,0 @@ -.mat-expansion-panel.exercise-hint-student-expansion-panel-wrapper { - .mat-expansion-panel-header { - padding: 10px 10px 10px 0; - font-weight: bold; - } - - .mat-expansion-panel-header-title { - flex-grow: 0; - } - - .mat-expansion-panel-header-description { - flex-grow: 3; - } - - .task-name { - font-size: 12px; - font-weight: 300; - } - - .mat-expansion-panel-body { - padding: 10px 0 0; - } - - .activated { - background-color: var(--info); - } - - .not_activated { - background-color: var(--success); - } -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-expandable.component.ts b/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-expandable.component.ts deleted file mode 100644 index bc787e798f87..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-expandable.component.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core'; -import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; - -import { ExerciseHint, HintType } from 'app/entities/hestia/exercise-hint.model'; -import { ExerciseHintResponse, ExerciseHintService } from 'app/exercises/shared/exercise-hint/shared/exercise-hint.service'; -import { StarRatingComponent } from 'app/exercises/shared/rating/star-rating/star-rating.component'; - -/** - * This component is a modal that shows the exercise's hints. - */ -@Component({ - selector: 'jhi-exercise-hint-expandable', - templateUrl: './exercise-hint-expandable.component.html', - styleUrls: ['./exercise-hint-expandable.component.scss'], - encapsulation: ViewEncapsulation.None, -}) -export class ExerciseHintExpandableComponent { - @Input() exerciseHint: ExerciseHint; - @Input() hasUsed: boolean; - @Output() - onHintActivated = new EventEmitter(); - - expanded = false; - isLoading = false; - - faQuestionCircle = faQuestionCircle; - - readonly HintType = HintType; - - constructor(private exerciseHintService: ExerciseHintService) {} - - displayHintContent() { - this.expanded = true; - - if (this.hasUsed) { - // the hint already contains the content - return; - } - - this.isLoading = true; - this.exerciseHintService.activateExerciseHint(this.exerciseHint!.exercise!.id!, this.exerciseHint!.id!).subscribe((res: ExerciseHintResponse) => { - this.exerciseHint = res.body!; - this.hasUsed = true; - this.isLoading = false; - this.onHintActivated.emit(this.exerciseHint); - }); - } - - collapseContent() { - this.expanded = false; - } - - onRate(event: { oldValue: number; newValue: number; starRating: StarRatingComponent }) { - this.exerciseHintService.rateExerciseHint(this.exerciseHint!.exercise!.id!, this.exerciseHint!.id!, event.newValue).subscribe(() => { - this.exerciseHint.currentUserRating = event.newValue; - }); - } -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-participation.module.ts b/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-participation.module.ts deleted file mode 100644 index 3742abfc8de0..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-participation.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { NgModule } from '@angular/core'; -import { MatExpansionModule } from '@angular/material/expansion'; -import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { ExerciseHintStudentDialogComponent } from 'app/exercises/shared/exercise-hint/participate/exercise-hint-student-dialog.component'; -import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; -import { ArtemisExerciseHintManagementModule } from 'app/exercises/shared/exercise-hint/manage/exercise-hint-management.module'; -import { ArtemisExerciseHintSharedModule } from 'app/exercises/shared/exercise-hint/shared/exercise-hint-shared.module'; -import { ExerciseHintExpandableComponent } from 'app/exercises/shared/exercise-hint/participate/exercise-hint-expandable.component'; -import { ExerciseHintButtonOverlayComponent } from 'app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component'; -import { RatingModule } from 'app/exercises/shared/rating/rating.module'; -import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; - -@NgModule({ - imports: [ - ArtemisSharedModule, - ArtemisMarkdownModule, - ArtemisExerciseHintManagementModule, - ArtemisExerciseHintSharedModule, - RatingModule, - MatExpansionModule, - ArtemisSharedComponentModule, - ], - declarations: [ExerciseHintStudentDialogComponent, ExerciseHintExpandableComponent, ExerciseHintButtonOverlayComponent], - exports: [ExerciseHintStudentDialogComponent, ExerciseHintExpandableComponent, ExerciseHintExpandableComponent, ExerciseHintButtonOverlayComponent], -}) -export class ArtemisExerciseHintParticipationModule {} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-student-dialog.component.html b/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-student-dialog.component.html deleted file mode 100644 index 09c17845b507..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-student-dialog.component.html +++ /dev/null @@ -1,32 +0,0 @@ - - diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-student-dialog.component.ts b/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-student-dialog.component.ts deleted file mode 100644 index 4e5d174b28bd..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-student-dialog.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; - -/** - * This component is a modal that shows the exercise's hints. - */ -@Component({ - selector: 'jhi-exercise-hint-student-dialog', - templateUrl: './exercise-hint-student-dialog.component.html', -}) -export class ExerciseHintStudentDialogComponent { - @Input() availableExerciseHints: ExerciseHint[]; - @Input() activatedExerciseHints: ExerciseHint[]; - @Output() - onHintActivated = new EventEmitter(); - - constructor(public activeModal: NgbActiveModal) {} - - /** - * Dismisses the modal - */ - clear() { - this.activeModal.dismiss('cancel'); - } -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-student-dialog.scss b/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-student-dialog.scss deleted file mode 100644 index a077cc1f55cd..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/participate/exercise-hint-student-dialog.scss +++ /dev/null @@ -1,4 +0,0 @@ -.hint-icon { - font-size: 1.5em; - cursor: pointer; -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/services/code-hint-cast.pipe.ts b/src/main/webapp/app/exercises/shared/exercise-hint/services/code-hint-cast.pipe.ts deleted file mode 100644 index c648527cef38..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/services/code-hint-cast.pipe.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; -import { CodeHint } from 'app/entities/hestia/code-hint-model'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; - -/** - * Pipe to transform an ExerciseHint into a CodeHint - */ -@Pipe({ - name: 'castToCodeHint', - pure: true, -}) -export class CastToCodeHintPipe implements PipeTransform { - transform(exerciseHint: ExerciseHint): CodeHint { - return exerciseHint as CodeHint; - } -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/services/code-hint.service.ts b/src/main/webapp/app/exercises/shared/exercise-hint/services/code-hint.service.ts deleted file mode 100644 index 2796e41b9f2c..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/services/code-hint.service.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { CodeHint } from 'app/entities/hestia/code-hint-model'; -import { createRequestOption } from 'app/shared/util/request.util'; - -export interface ICodeHintService { - /** - * Generates the code hints for a programming exercise - * @param exerciseId Id of the programming exercise - * @param deleteOldCodeHints Whether the old code hints should be deleted - */ - generateCodeHintsForExercise(exerciseId: number, deleteOldCodeHints: boolean): Observable; - - /** - * Generates the description for a code hint using Iris - * @param exerciseId of the programming exercise - * @param codeHintId of the code hint for which the description will be generated - */ - generateDescriptionForCodeHint(exerciseId: number, codeHintId: number): Observable>; - - /** - * Removes a programming exercise solution entry from a code hint - * @param exerciseId of the programming exercise - * @param codeHintId of the code hint from which the solution entry will be removed - * @param solutionEntryId of the solution entry to be removed - */ - removeSolutionEntryFromCodeHint(exerciseId: number, codeHintId: number, solutionEntryId: number): Observable>; -} - -@Injectable({ providedIn: 'root' }) -export class CodeHintService implements ICodeHintService { - public resourceUrl = 'api/programming-exercises'; - - constructor(protected http: HttpClient) {} - - /** - * Generates the code hints for a programming exercise - * @param exerciseId Id of the programming exercise - * @param deleteOldCodeHints Whether the old code hints should be deleted - */ - generateCodeHintsForExercise(exerciseId: number, deleteOldCodeHints: boolean): Observable { - const options = createRequestOption({ deleteOldCodeHints }); - return this.http.post(`${this.resourceUrl}/${exerciseId}/code-hints`, undefined, { - params: options, - }); - } - - /** - * Generates the description for a code hint using Iris - * @param exerciseId of the programming exercise - * @param codeHintId of the code hint for which the description will be generated - */ - generateDescriptionForCodeHint(exerciseId: number, codeHintId: number): Observable> { - return this.http.post(`${this.resourceUrl}/${exerciseId}/code-hints/${codeHintId}/generate-description`, undefined, { observe: 'response' }); - } - - /** - * Removes a programming exercise solution entry from a code hint. Only removes the linkage between, but does not - * delete the entry itself. - * @param exerciseId of the programming exercise - * @param codeHintId of the code hint from which the solution entry will be removed - * @param solutionEntryId of the solution entry to be removed - */ - removeSolutionEntryFromCodeHint(exerciseId: number, codeHintId: number, solutionEntryId: number): Observable> { - return this.http.delete(`${this.resourceUrl}/${exerciseId}/code-hints/${codeHintId}/solution-entries/${solutionEntryId}`, { observe: 'response' }); - } -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/services/programming-exercise-solution-entry.service.ts b/src/main/webapp/app/exercises/shared/exercise-hint/services/programming-exercise-solution-entry.service.ts deleted file mode 100644 index 45af7c818acb..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/services/programming-exercise-solution-entry.service.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; - -export interface IProgrammingExerciseSolutionEntryService { - /** - * Create a custom solution entry for a programming exercise and test case. - * @param exerciseId of the programming exercise - * @param testCaseId of the test case that the entry relates to - * @param entry the entry to be created - */ - createSolutionEntry(exerciseId: number, testCaseId: number, entry: ProgrammingExerciseSolutionEntry): Observable; - - /** - * Get all solution entries for a programming exercise. - * @param exerciseId of the programming exercise - */ - getSolutionEntriesForExercise(exerciseId: number): Observable; - - /** - * Update a solution entry and returns the updated entry. - * @param exerciseId of the programming exercise - * @param testCaseId of the test case - * @param solutionEntryId of the solution entry to update - * @param entry the entry to update - */ - updateSolutionEntry(exerciseId: number, testCaseId: number, solutionEntryId: number, entry: ProgrammingExerciseSolutionEntry): Observable; - - /** - * Delete a solution entry. - * @param exerciseId of the programming exercise - * @param testCaseId of the test case - * @param solutionEntryId of the solution entry to delete - */ - deleteSolutionEntry(exerciseId: number, testCaseId: number, solutionEntryId: number): Observable; - - /** - * Delete all solution entries for a programming exercise. - * @param exerciseId of the programming exercise - */ - deleteAllSolutionEntriesForExercise(exerciseId: number): Observable; -} - -@Injectable({ providedIn: 'root' }) -export class ProgrammingExerciseSolutionEntryService implements IProgrammingExerciseSolutionEntryService { - public resourceUrl = 'api/programming-exercises'; - - constructor(protected http: HttpClient) {} - - /** - * Create a custom solution entry for a programming exercise and test case. - * @param exerciseId of the programming exercise - * @param testCaseId of the test case that the entry relates to - * @param entry the entry to be created - */ - createSolutionEntry(exerciseId: number, testCaseId: number, entry: ProgrammingExerciseSolutionEntry): Observable { - return this.http.post(`${this.resourceUrl}/${exerciseId}/test-cases/${testCaseId}/solution-entries`, entry); - } - - /** - * Get all solution entries for a programming exercise. - * @param exerciseId of the programming exercise - */ - getSolutionEntriesForExercise(exerciseId: number): Observable { - return this.http.get(`${this.resourceUrl}/${exerciseId}/solution-entries`); - } - - /** - * Update a solution entry and returns the updated entry. - * @param exerciseId of the programming exercise - * @param testCaseId of the test case - * @param solutionEntryId of the solution entry to update - * @param entry the entry to update - */ - updateSolutionEntry(exerciseId: number, testCaseId: number, solutionEntryId: number, entry: ProgrammingExerciseSolutionEntry): Observable { - return this.http.put(`${this.resourceUrl}/${exerciseId}/test-cases/${testCaseId}/solution-entries/${solutionEntryId}`, entry); - } - - /** - * Delete a solution entry. - * @param exerciseId of the programming exercise - * @param testCaseId of the test case - * @param solutionEntryId of the solution entry to delete - */ - deleteSolutionEntry(exerciseId: number, testCaseId: number, solutionEntryId: number) { - return this.http.delete(`${this.resourceUrl}/${exerciseId}/test-cases/${testCaseId}/solution-entries/${solutionEntryId}`); - } - - /** - * Delete all solution entries for a programming exercise. - * @param exerciseId of the programming exercise - */ - deleteAllSolutionEntriesForExercise(exerciseId: number) { - return this.http.delete(`${this.resourceUrl}/${exerciseId}/solution-entries`); - } -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/shared/code-hint-container.component.html b/src/main/webapp/app/exercises/shared/exercise-hint/shared/code-hint-container.component.html deleted file mode 100644 index d62c18f4e20e..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/shared/code-hint-container.component.html +++ /dev/null @@ -1,25 +0,0 @@ -@for (solutionEntry of sortedSolutionEntries; track solutionEntry) { -
-
- - @if (enableEditing) { -
- -
- } -
-
-} -@if (!sortedSolutionEntries.length) { -
- -
-} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/shared/code-hint-container.component.ts b/src/main/webapp/app/exercises/shared/exercise-hint/shared/code-hint-container.component.ts deleted file mode 100644 index 8bd962711fdb..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/shared/code-hint-container.component.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { Subject } from 'rxjs'; - -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { CodeHint } from 'app/entities/hestia/code-hint-model'; -import { CodeHintService } from 'app/exercises/shared/exercise-hint/services/code-hint.service'; -import { faTimes } from '@fortawesome/free-solid-svg-icons'; - -/** - * Component containing the solution entries for a {@link CodeHint}. - * The entries are sorted by name (primary) and start line number (secondary) - */ -@Component({ - selector: 'jhi-code-hint-container', - templateUrl: './code-hint-container.component.html', -}) -export class CodeHintContainerComponent implements OnInit, OnDestroy { - @Input() - codeHint: CodeHint; - - @Input() - enableEditing = false; - - sortedSolutionEntries: ProgrammingExerciseSolutionEntry[]; - - private dialogErrorSource = new Subject(); - dialogError$ = this.dialogErrorSource.asObservable(); - - faTimes = faTimes; - - constructor( - protected route: ActivatedRoute, - private codeHintService: CodeHintService, - ) {} - - ngOnInit() { - this.setSortedSolutionEntriesForCodeHint(); - } - - ngOnDestroy(): void { - this.dialogErrorSource.unsubscribe(); - } - - setSortedSolutionEntriesForCodeHint() { - this.sortedSolutionEntries = - this.codeHint.solutionEntries?.sort((a, b) => { - return a.filePath?.localeCompare(b.filePath!) || a.line! - b.line!; - }) ?? []; - } - - /** - * Removes a solution entry from the code hint - * @param solutionEntryId of the solution entry to be removed - */ - removeEntryFromCodeHint(solutionEntryId: number) { - this.codeHintService.removeSolutionEntryFromCodeHint(this.codeHint.exercise!.id!, this.codeHint.id!, solutionEntryId).subscribe({ - next: () => { - this.sortedSolutionEntries = this.sortedSolutionEntries.filter((entry) => entry.id !== solutionEntryId); - this.codeHint.solutionEntries = this.sortedSolutionEntries; - this.dialogErrorSource.next(''); - }, - error: (error) => { - this.dialogErrorSource.next(error.message); - }, - }); - } -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/shared/exercise-hint-shared.module.ts b/src/main/webapp/app/exercises/shared/exercise-hint/shared/exercise-hint-shared.module.ts deleted file mode 100644 index 08a2b1c8ee07..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/shared/exercise-hint-shared.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NgModule } from '@angular/core'; -import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { CastToCodeHintPipe } from 'app/exercises/shared/exercise-hint/services/code-hint-cast.pipe'; -import { SolutionEntryComponent } from 'app/exercises/shared/exercise-hint/shared/solution-entry.component'; -import { CodeHintContainerComponent } from 'app/exercises/shared/exercise-hint/shared/code-hint-container.component'; -import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; -import { MonacoEditorComponent } from 'app/shared/monaco-editor/monaco-editor.component'; - -@NgModule({ - imports: [ArtemisSharedModule, ArtemisMarkdownModule, MonacoEditorComponent], - declarations: [SolutionEntryComponent, CodeHintContainerComponent, CastToCodeHintPipe], - exports: [SolutionEntryComponent, CodeHintContainerComponent, CastToCodeHintPipe], -}) -export class ArtemisExerciseHintSharedModule {} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/shared/exercise-hint.service.ts b/src/main/webapp/app/exercises/shared/exercise-hint/shared/exercise-hint.service.ts deleted file mode 100644 index 88c29bef59a6..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/shared/exercise-hint.service.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable, tap } from 'rxjs'; - -import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; -import { ExerciseHint, HintType } from 'app/entities/hestia/exercise-hint.model'; -import { EntityTitleService, EntityType } from 'app/shared/layouts/navbar/entity-title.service'; - -export type ExerciseHintResponse = HttpResponse; - -export interface IExerciseHintService { - /** - * Creates an exercise hint - * @param exerciseId of the exercise - * @param exerciseHint Exercise hint to create - */ - create(exerciseId: number, exerciseHint: ExerciseHint): Observable; - - /** - * Updates an exercise hint - * @param exerciseId of the exercise - * @param exerciseHint Exercise hint to update - */ - update(exerciseId: number, exerciseHint: ExerciseHint): Observable; - - /** - * Deletes an exercise hint - * @param exerciseId Id of the exercise of which to delete the hint - * @param exerciseHintId Id of exercise hint to delete - */ - delete(exerciseId: number, exerciseHintId: number): Observable>; - - /** - * Finds an exercise hint - * @param exerciseId Id of the exercise of which to retrieve the hint - * @param exerciseHintId Id of exercise hint to find - */ - find(exerciseId: number, exerciseHintId: number): Observable; - - /** - * Finds all exercise hints by exercise id - * @param exerciseId Id of exercise - */ - findByExerciseId(exerciseId: number): Observable>; - - /** - * Gets all available exercise hints - * @param exerciseId Id of the exercise of which to retrieve all available exercise hints - */ - getAvailableExerciseHints(exerciseId: number): Observable>; - - /** - * Gets all activated exercise hints - * @param exerciseId Id of the exercise of which to retrieve all activated exercise hints - */ - getActivatedExerciseHints(exerciseId: number): Observable>; - - /** - * Activates an exercise hint for the current user - * @param exerciseId Id of the exercise - * @param exerciseHintId Id of the exercise hint - */ - activateExerciseHint(exerciseId: number, exerciseHintId: number): Observable; - - /** - * Activates an exercise hint for the current user - * @param exerciseId Id of the exercise - * @param exerciseHintId Id of the exercise hint - * @param ratingValue Value of the rating - */ - rateExerciseHint(exerciseId: number, exerciseHintId: number, ratingValue: number): Observable>; -} - -@Injectable({ providedIn: 'root' }) -export class ExerciseHintService implements IExerciseHintService { - public resourceUrl = 'api/programming-exercises'; - - constructor( - protected http: HttpClient, - private entityTitleService: EntityTitleService, - ) {} - - /** - * Creates an exercise hint - * @param exerciseId of the exercise - * @param exerciseHint Exercise hint to create - */ - create(exerciseId: number, exerciseHint: ExerciseHint): Observable { - exerciseHint.exercise = ExerciseService.convertExerciseDatesFromClient(exerciseHint.exercise!); - exerciseHint.type = HintType.TEXT; - if (exerciseHint.exercise.categories) { - exerciseHint.exercise.categories = ExerciseService.stringifyExerciseCategories(exerciseHint.exercise); - } - return this.http.post(`${this.resourceUrl}/${exerciseId}/exercise-hints`, exerciseHint, { observe: 'response' }); - } - - /** - * Updates an exercise hint - * @param exerciseId of the exercise - * @param exerciseHint Exercise hint to update - */ - update(exerciseId: number, exerciseHint: ExerciseHint): Observable { - exerciseHint.exercise = ExerciseService.convertExerciseDatesFromClient(exerciseHint.exercise!); - exerciseHint.exercise.categories = ExerciseService.stringifyExerciseCategories(exerciseHint.exercise); - return this.http.put(`${this.resourceUrl}/${exerciseId}/exercise-hints/${exerciseHint.id}`, exerciseHint, { observe: 'response' }); - } - - /** - * Deletes an exercise hint - * @param exerciseId Id of the exercise of which to delete the hint - * @param exerciseHintId Id of exercise hint to delete - */ - delete(exerciseId: number, exerciseHintId: number): Observable> { - return this.http.delete(`${this.resourceUrl}/${exerciseId}/exercise-hints/${exerciseHintId}`, { observe: 'response' }); - } - - /** - * Finds an exercise hint - * @param exerciseId Id of the exercise of which to retrieve the hint - * @param exerciseHintId Id of exercise hint to find - */ - find(exerciseId: number, exerciseHintId: number): Observable { - return this.http - .get(`${this.resourceUrl}/${exerciseId}/exercise-hints/${exerciseHintId}`, { observe: 'response' }) - .pipe(tap((res) => this.sendTitlesToEntityTitleService(res?.body, exerciseId))); - } - - /** - * Finds all exercise hints by exercise id - * Also fetches any relations. This currently only includes the submission entries of a code hint - * @param exerciseId Id of exercise - */ - findByExerciseId(exerciseId: number): Observable> { - return this.http - .get(`${this.resourceUrl}/${exerciseId}/exercise-hints`, { observe: 'response' }) - .pipe(tap((res) => res?.body?.forEach((hint) => this.sendTitlesToEntityTitleService(hint, exerciseId)))); - } - - /** - * Gets all available exercise hints - * @param exerciseId Id of the exercise of which to retrieve all available exercise hints - */ - getAvailableExerciseHints(exerciseId: number): Observable> { - return this.http.get(`${this.resourceUrl}/${exerciseId}/exercise-hints/available`, { observe: 'response' }); - } - - /** - * Gets all activated exercise hints - * @param exerciseId Id of the exercise of which to retrieve all activated exercise hints - */ - getActivatedExerciseHints(exerciseId: number): Observable> { - return this.http - .get(`${this.resourceUrl}/${exerciseId}/exercise-hints/activated`, { observe: 'response' }) - .pipe(tap((res) => res?.body?.forEach((hint) => this.sendTitlesToEntityTitleService(hint, exerciseId)))); - } - - /** - * Activates an exercise hint for the current user - * @param exerciseId Id of the exercise - * @param exerciseHintId Id of the exercise hint - */ - activateExerciseHint(exerciseId: number, exerciseHintId: number): Observable { - return this.http.post(`${this.resourceUrl}/${exerciseId}/exercise-hints/${exerciseHintId}/activate`, {}, { observe: 'response' }); - } - - /** - * Activates an exercise hint for the current user - * @param exerciseId Id of the exercise - * @param exerciseHintId Id of the exercise hint - * @param ratingValue Value of the rating - */ - rateExerciseHint(exerciseId: number, exerciseHintId: number, ratingValue: number): Observable> { - return this.http.post(`${this.resourceUrl}/${exerciseId}/exercise-hints/${exerciseHintId}/rating/${ratingValue}`, {}, { observe: 'response' }); - } - - private sendTitlesToEntityTitleService(hint: ExerciseHint | undefined | null, exerciseId: number) { - this.entityTitleService.setTitle(EntityType.HINT, [hint?.id, exerciseId], hint?.title); - } -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/shared/solution-entry.component.html b/src/main/webapp/app/exercises/shared/exercise-hint/shared/solution-entry.component.html deleted file mode 100644 index 28a935fd18ff..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/shared/solution-entry.component.html +++ /dev/null @@ -1,12 +0,0 @@ -
-
{{ solutionEntry.filePath }}
-
-
- -
diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/shared/solution-entry.component.scss b/src/main/webapp/app/exercises/shared/exercise-hint/shared/solution-entry.component.scss deleted file mode 100644 index 5a51c7d082f5..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/shared/solution-entry.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.solution-entry-editor { - border: 1px solid var(--border-color); -} diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/shared/solution-entry.component.ts b/src/main/webapp/app/exercises/shared/exercise-hint/shared/solution-entry.component.ts deleted file mode 100644 index 314fce704776..000000000000 --- a/src/main/webapp/app/exercises/shared/exercise-hint/shared/solution-entry.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; -import { faTimes } from '@fortawesome/free-solid-svg-icons'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { MonacoEditorComponent } from 'app/shared/monaco-editor/monaco-editor.component'; - -@Component({ - selector: 'jhi-solution-entry', - templateUrl: './solution-entry.component.html', - styleUrls: ['./solution-entry.component.scss'], -}) -export class SolutionEntryComponent implements OnInit { - @ViewChild('editor', { static: true }) - editor: MonacoEditorComponent; - - @Input() - solutionEntry: ProgrammingExerciseSolutionEntry; - @Input() - enableEditing: boolean; - - @Output() - onRemoveEntry: EventEmitter = new EventEmitter(); - - editorHeight = 20; - - protected readonly faTimes = faTimes; - - ngOnInit(): void { - this.setupEditor(); - } - - onEditorContentChange(value: string): void { - this.solutionEntry.code = value; - } - - onContentSizeChange(contentHeight: number): void { - this.editorHeight = contentHeight; - } - - setupEditor(): void { - const startLine = this.solutionEntry.line ?? 1; - this.editor.setStartLineNumber(startLine); - this.editor.changeModel(this.solutionEntry.filePath ?? 'file', this.solutionEntry.code ?? ''); - this.editor.layout(); - // We manually fetch the initial content height, as the editor does not provide it immediately - this.editorHeight = this.editor.getContentHeight(); - } -} diff --git a/src/main/webapp/app/exercises/shared/exercise/exercise.service.ts b/src/main/webapp/app/exercises/shared/exercise/exercise.service.ts index f3247bc26267..b377e0c20572 100644 --- a/src/main/webapp/app/exercises/shared/exercise/exercise.service.ts +++ b/src/main/webapp/app/exercises/shared/exercise/exercise.service.ts @@ -20,7 +20,6 @@ import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { SafeHtml } from '@angular/platform-browser'; import { PlagiarismCaseInfo } from 'app/exercises/shared/plagiarism/types/PlagiarismCaseInfo'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; import { IrisExerciseSettings } from 'app/entities/iris/settings/iris-settings.model'; export type EntityResponseType = HttpResponse; @@ -38,8 +37,6 @@ export type ExerciseDetailsType = { exercise: Exercise; irisSettings?: IrisExerciseSettings; plagiarismCaseInfo?: PlagiarismCaseInfo; - availableExerciseHints?: ExerciseHint[]; - activatedExerciseHints?: ExerciseHint[]; }; export type CourseExistingExerciseDetailsType = { @@ -176,9 +173,6 @@ export class ExerciseService { if (res.body.exercise.posts === undefined) { res.body.exercise.posts = []; } - for (const hint of res.body.activatedExerciseHints ?? []) { - this.entityTitleService.setTitle(EntityType.HINT, [hint?.id, exerciseId], hint?.title); - } } return res; }), diff --git a/src/main/webapp/app/localvc/commit-details-view/commit-details-view.component.ts b/src/main/webapp/app/localvc/commit-details-view/commit-details-view.component.ts index 98ced261dc32..b147bd0892cd 100644 --- a/src/main/webapp/app/localvc/commit-details-view/commit-details-view.component.ts +++ b/src/main/webapp/app/localvc/commit-details-view/commit-details-view.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffReport } from 'app/entities/programming-exercise-git-diff-report.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import { Subscription, throwError } from 'rxjs'; diff --git a/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.html b/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.html index 55391f36192e..05e6e4d9d53e 100644 --- a/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.html +++ b/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.html @@ -190,12 +190,6 @@

@if (exercise.type === PROGRAMMING && !exercise.exerciseGroup && irisSettings?.irisChatSettings?.enabled) { } - @if (plagiarismCaseInfo?.verdict === PlagiarismVerdict.NO_PLAGIARISM) { diff --git a/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.ts b/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.ts index 330e469035a6..6b81fedc9ad0 100644 --- a/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.ts +++ b/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.ts @@ -30,11 +30,8 @@ import { Complaint } from 'app/entities/complaint.model'; import { SubmissionPolicy } from 'app/entities/submission-policy.model'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { IconDefinition, faAngleDown, faAngleUp, faBook, faEye, faFileSignature, faListAlt, faSignal, faTable, faWrench } from '@fortawesome/free-solid-svg-icons'; -import { ExerciseHintService } from 'app/exercises/shared/exercise-hint/shared/exercise-hint.service'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; import { PlagiarismVerdict } from 'app/exercises/shared/plagiarism/types/PlagiarismVerdict'; import { PlagiarismCaseInfo } from 'app/exercises/shared/plagiarism/types/PlagiarismCaseInfo'; -import { ResultService } from 'app/exercises/shared/result/result.service'; import { MAX_RESULT_HISTORY_LENGTH } from 'app/overview/result-history/result-history.component'; import { isCommunicationEnabled, isMessagingEnabled } from 'app/entities/course.model'; import { ExerciseCacheService } from 'app/exercises/shared/exercise/exercise-cache.service'; @@ -68,7 +65,6 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp readonly MODELING = ExerciseType.MODELING; readonly TEXT = ExerciseType.TEXT; readonly FILE_UPLOAD = ExerciseType.FILE_UPLOAD; - readonly evaluateBadge = ResultService.evaluateBadge; readonly dayjs = dayjs; readonly ChatServiceMode = ChatServiceMode; @@ -99,8 +95,6 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp submissionPolicy?: SubmissionPolicy; exampleSolutionCollapsed: boolean; plagiarismCaseInfo?: PlagiarismCaseInfo; - availableExerciseHints: ExerciseHint[]; - activatedExerciseHints: ExerciseHint[]; irisSettings?: IrisSettings; paramsSubscription: Subscription; profileSubscription?: Subscription; @@ -120,8 +114,6 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp faWrench = faWrench; faTable = faTable; faListAlt = faListAlt; - faSignal = faSignal; - faFileSignature = faFileSignature; faAngleDown = faAngleDown; faAngleUp = faAngleUp; @@ -137,7 +129,6 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp private quizExerciseService: QuizExerciseService, private complaintService: ComplaintService, private artemisMarkdown: ArtemisMarkdownService, - private exerciseHintService: ExerciseHintService, scienceService: ScienceService, ) { super(scienceService, ScienceEventType.EXERCISE__OPEN); @@ -209,8 +200,6 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp this.irisSettings = newExerciseDetails.irisSettings; } }); - this.availableExerciseHints = newExerciseDetails.availableExerciseHints || []; - this.activatedExerciseHints = newExerciseDetails.activatedExerciseHints || []; } this.showIfExampleSolutionPresent(newExerciseDetails.exercise); @@ -331,25 +320,6 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp } this.updateStudentParticipations(); this.mergeResultsAndSubmissionsForParticipations(); - - if (ExerciseType.PROGRAMMING === this.exercise?.type) { - this.exerciseHintService.getActivatedExerciseHints(this.exerciseId).subscribe((activatedRes?: HttpResponse) => { - this.activatedExerciseHints = activatedRes!.body!; - - this.exerciseHintService.getAvailableExerciseHints(this.exerciseId).subscribe((availableRes?: HttpResponse) => { - // filter out the activated hints from the available hints - this.availableExerciseHints = availableRes!.body!.filter( - (availableHint) => !this.activatedExerciseHints.some((activatedHint) => availableHint.id === activatedHint.id), - ); - const filteredAvailableExerciseHints = this.availableExerciseHints.filter((hint) => hint.displayThreshold !== 0); - if (filteredAvailableExerciseHints.length) { - this.alertService.info('artemisApp.exerciseHint.availableHintsAlertMessage', { - taskName: filteredAvailableExerciseHints.first()?.programmingExerciseTask?.taskName, - }); - } - }); - }); - } } }); } @@ -429,11 +399,6 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp this.alertService.error(error); } - onHintActivated(exerciseHint: ExerciseHint) { - this.availableExerciseHints = this.availableExerciseHints.filter((hint) => hint.id !== exerciseHint.id); - this.activatedExerciseHints.push(exerciseHint); - } - /** * Used to change the boolean value for the example solution dropdown menu */ diff --git a/src/main/webapp/app/overview/exercise-details/course-exercise-details.module.ts b/src/main/webapp/app/overview/exercise-details/course-exercise-details.module.ts index 6c5664b7bff2..ec6495463ecc 100644 --- a/src/main/webapp/app/overview/exercise-details/course-exercise-details.module.ts +++ b/src/main/webapp/app/overview/exercise-details/course-exercise-details.module.ts @@ -25,7 +25,6 @@ import { SubmissionResultStatusModule } from 'app/overview/submission-result-sta import { LtiInitializerComponent } from 'app/overview/exercise-details/lti-initializer.component'; import { LtiInitializerModalComponent } from 'app/overview/exercise-details/lti-initializer-modal.component'; import { ArtemisProgrammingExerciseManagementModule } from 'app/exercises/programming/manage/programming-exercise-management.module'; -import { ArtemisExerciseHintParticipationModule } from 'app/exercises/shared/exercise-hint/participate/exercise-hint-participation.module'; import { ProblemStatementComponent } from 'app/overview/exercise-details/problem-statement/problem-statement.component'; import { ArtemisFeedbackModule } from 'app/exercises/shared/feedback/feedback.module'; import { ArtemisExerciseInfoModule } from 'app/exercises/shared/exercise-info/exercise-info.module'; @@ -69,7 +68,6 @@ const standaloneComponents = [ExerciseHeadersInformationComponent]; ArtemisMarkdownModule, SubmissionResultStatusModule, ArtemisProgrammingExerciseManagementModule, - ArtemisExerciseHintParticipationModule, ArtemisFeedbackModule, ArtemisExerciseInfoModule, IrisModule, diff --git a/src/main/webapp/app/shared/http/file.service.ts b/src/main/webapp/app/shared/http/file.service.ts index 1c9f264f41d3..f739c44014ed 100644 --- a/src/main/webapp/app/shared/http/file.service.ts +++ b/src/main/webapp/app/shared/http/file.service.ts @@ -37,32 +37,11 @@ export class FileService { return Promise.resolve(file); } - /** - * Fetches the aeolus template file for the given programming language - * @param {ProgrammingLanguage} language - * @param {ProjectType} projectType (if available) - * @param staticAnalysis (if available) whether static code analysis should be enabled - * @param sequentialRuns (if available) whether sequential test runs should be enabled - * @param coverage (if available) whether test coverage should be enabled - * @returns json test file - */ - getAeolusTemplateFile(language: ProgrammingLanguage, projectType?: ProjectType, staticAnalysis?: boolean, sequentialRuns?: boolean, coverage?: boolean): Observable { - const urlParts: string[] = [language]; - const params: string[] = []; - if (projectType) { - urlParts.push(projectType); - } - params.push('staticAnalysis=' + (staticAnalysis == undefined ? false : staticAnalysis)); - params.push('sequentialRuns=' + (sequentialRuns == undefined ? false : sequentialRuns)); - params.push('testCoverage=' + (coverage == undefined ? false : coverage)); - return this.http.get(`${this.resourceUrl}/aeolus/templates/` + urlParts.join('/') + '?' + params.join('&'), { responseType: 'text' as 'json' }); - } - /** * Fetches the template code of conduct * @returns markdown file */ - getTemplateCodeOfCondcut(): Observable> { + getTemplateCodeOfConduct(): Observable> { return this.http.get(`api/files/templates/code-of-conduct`, { observe: 'response', responseType: 'text' as 'json' }); } diff --git a/src/main/webapp/app/shared/layouts/navbar/entity-title.service.ts b/src/main/webapp/app/shared/layouts/navbar/entity-title.service.ts index a50aab27ddc4..3ab54964001f 100644 --- a/src/main/webapp/app/shared/layouts/navbar/entity-title.service.ts +++ b/src/main/webapp/app/shared/layouts/navbar/entity-title.service.ts @@ -9,7 +9,6 @@ export enum EntityType { EXERCISE = 'EXERCISE', LECTURE = 'LECTURE', COMPETENCY = 'COMPETENCY', - HINT = 'HINT', DIAGRAM = 'DIAGRAM', ORGANIZATION = 'ORGANIZATION', EXAM = 'EXAM', @@ -120,9 +119,6 @@ export class EntityTitleService { case EntityType.COMPETENCY: resourceUrl += 'competencies'; break; - case EntityType.HINT: - resourceUrl += `programming-exercises/${ids[1]}/exercise-hints`; - break; case EntityType.DIAGRAM: resourceUrl += 'apollon-diagrams'; break; diff --git a/src/main/webapp/app/shared/layouts/navbar/navbar.component.ts b/src/main/webapp/app/shared/layouts/navbar/navbar.component.ts index cdf3bbb137ae..77a83dc0990d 100644 --- a/src/main/webapp/app/shared/layouts/navbar/navbar.component.ts +++ b/src/main/webapp/app/shared/layouts/navbar/navbar.component.ts @@ -342,7 +342,6 @@ export class NavbarComponent implements OnInit, OnDestroy { detailed: 'artemisApp.gradingSystem.detailedTab.title', interval: 'artemisApp.gradingSystem.intervalTab.title', plagiarism_cases: 'artemisApp.plagiarism.cases.pageTitle', - code_hint_management: 'artemisApp.codeHint.management.title', tutorial_groups_management: 'artemisApp.pages.tutorialGroupsManagement.title', tutorial_groups: 'artemisApp.breadcrumb.title', registered_students: 'artemisApp.pages.registeredStudents.title', @@ -492,13 +491,6 @@ export class NavbarComponent implements OnInit, OnDestroy { case 'assessment-dashboard': this.addResolvedTitleAsCrumb(EntityType.EXERCISE, [Number(segment)], currentPath, segment); break; - case 'exercise-hints': - // obtain the exerciseId of the current path - // current path of form '/course-management/:courseId/exercises/:exerciseId/... - - const exerciseId = currentPath.split('/')[4]; - this.addResolvedTitleAsCrumb(EntityType.HINT, [Number(segment), Number(exerciseId)], currentPath, segment); - break; case 'apollon-diagrams': this.addResolvedTitleAsCrumb(EntityType.DIAGRAM, [Number(segment)], currentPath, segment); break; diff --git a/src/main/webapp/app/shared/markdown-editor/monaco/markdown-editor-monaco.component.ts b/src/main/webapp/app/shared/markdown-editor/monaco/markdown-editor-monaco.component.ts index 05acc805c47a..4be6605a93b9 100644 --- a/src/main/webapp/app/shared/markdown-editor/monaco/markdown-editor-monaco.component.ts +++ b/src/main/webapp/app/shared/markdown-editor/monaco/markdown-editor-monaco.component.ts @@ -278,7 +278,9 @@ export class MarkdownEditorMonacoComponent implements AfterContentInit, AfterVie color: this.filterDisplayedAction(this.colorAction), domain: { withoutOptions: this.filterDisplayedActions(this.domainActions.filter((action) => !(action instanceof TextEditorDomainActionWithOptions))), - withOptions: this.filterDisplayedActions(this.domainActions.filter((action) => action instanceof TextEditorDomainActionWithOptions)), + withOptions: this.filterDisplayedActions( + this.domainActions.filter((action) => action instanceof TextEditorDomainActionWithOptions), + ) as TextEditorDomainActionWithOptions[], }, lecture: this.filterDisplayedAction(this.lectureReferenceAction), meta: this.filterDisplayedActions(this.metaActions), @@ -521,7 +523,7 @@ export class MarkdownEditorMonacoComponent implements AfterContentInit, AfterVie private processFileUploadResponse(response: FileUploadResponse, file: File): void { const extension = file.name.split('.').last()?.toLocaleLowerCase(); - const attachmentAction: AttachmentAction | undefined = this.defaultActions.find((action) => action instanceof AttachmentAction); + const attachmentAction: AttachmentAction | undefined = this.defaultActions.find((action) => action instanceof AttachmentAction) as AttachmentAction; const urlAction: UrlAction | undefined = this.defaultActions.find((action) => action instanceof UrlAction); if (!attachmentAction || !urlAction || !response.path) { throw new Error('Cannot process file upload.'); diff --git a/src/main/webapp/content/scss/themes/_dark-variables.scss b/src/main/webapp/content/scss/themes/_dark-variables.scss index 19de3c6980ef..97c749b23ac0 100644 --- a/src/main/webapp/content/scss/themes/_dark-variables.scss +++ b/src/main/webapp/content/scss/themes/_dark-variables.scss @@ -452,7 +452,6 @@ $monaco-editor-build-annotation-outdated-background: scale-color($gray-500, $alp $monaco-editor-build-annotation-outdated-glyph: $gray-500; $monaco-editor-diff-highlight-green: $success; $monaco-editor-diff-line-highlight: scale-color($monaco-editor-diff-highlight-green, $alpha: -80%, $lightness: -20%); -$monaco-editor-test-coverage-highlight: scale-color($warning, $alpha: -80%); $monaco-editor-add-feedback-button-background: $gray-600; $monaco-editor-add-feedback-button-hover-background: lighten($gray-600, 10%); $monaco-editor-add-feedback-button-text: $gray-300; diff --git a/src/main/webapp/content/scss/themes/_default-variables.scss b/src/main/webapp/content/scss/themes/_default-variables.scss index f8bbfad5151a..d7aab8d31279 100644 --- a/src/main/webapp/content/scss/themes/_default-variables.scss +++ b/src/main/webapp/content/scss/themes/_default-variables.scss @@ -378,7 +378,6 @@ $monaco-editor-build-annotation-outdated-background: scale-color($gray-500, $alp $monaco-editor-build-annotation-outdated-glyph: $gray-500; $monaco-editor-diff-highlight-green: rgb(63, 185, 80); $monaco-editor-diff-line-highlight: scale-color($monaco-editor-diff-highlight-green, $alpha: -60%); -$monaco-editor-test-coverage-highlight: scale-color($warning, $alpha: -80%); $monaco-editor-add-feedback-button-background: $gray-400; $monaco-editor-add-feedback-button-hover-background: $gray-500; $monaco-editor-add-feedback-button-text: $black; diff --git a/src/main/webapp/i18n/de/codeHint.json b/src/main/webapp/i18n/de/codeHint.json deleted file mode 100644 index 93f2b508fea0..000000000000 --- a/src/main/webapp/i18n/de/codeHint.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "artemisApp": { - "codeHint": { - "type": "Code-Hinweis", - "entry": "Eintrag", - "entryType": "Typ (V/S)", - "lineOfCode": "1 Code-Zeile", - "linesOfCode": "{{ lines }} Code-Zeilen", - "entryTypeTooltip": "Definiert, ob dieser Eintrag für einen strukturellen (S) oder verhaltensweisen (V) Test erstellt wurde.", - "entryAbbreviation": { - "structural": "S", - "behavioral": "V" - }, - "entries": "Fragmente", - "removeEntryQuestion": "Soll dieses Fragment wirklich entfernt werden?", - "noEntries": "Dieser Code-Hinweis hat keine Fragmente.", - "deleted": "Exercise Hint gelöscht mit ID {{ param }}", - "management": { - "title": "Code-Hinweise verwalten", - "previousButton": { - "label": "Zurück" - }, - "nextButton": { - "label": "Weiter", - "tooltip": "Der aktuelle Schritt muss erfolgreich ausgeführt seien, damit fortgefahren werden kann." - }, - "step1": { - "name": "Git-Diff Report", - "description": "Der Git-Diff Report wird automatisch für das Template- und Lösungs-Repository erstellt.", - "notGenerated": "Git-Diff Report wurde noch nicht erzeugt." - }, - "step2": { - "name": "Code-Abdeckung", - "description": "Der Code-Abdeckungs-Report wird automatisch für jede Einreichung in Lösungs-Repository erstellt.", - "notGenerated": "Der Code-Abdeckungs-Report wurde noch nicht erzeugt." - }, - "step3": { - "name": "Lösungs-Fragmente", - "description": "In diesem Schritt kannst du die einzelnen Fragmente bearbeiten, aus denen Code-Hinweise erstellt werden.", - "notGenerated": "Bisher wurden keine Fragmente generiert.", - "createManualFragmentButton": { - "label": "Erstelle manuelles Fragment", - "tooltip": "Fragment manuell mit zugehörigem Test und Datei erstellen." - }, - "structuralEntriesButton": { - "label": "Strukturelle Fragmente generieren", - "tooltip": "Erstellt Fragmente für strukturelle Tests. Bereits existierende strukturelle Fragmente werden überschrieben." - }, - "behavioralEntriesButton": { - "label": "Verhaltensweise Fragmente generieren", - "tooltip": "Erstellt Fragmente für verhaltensweise Tests. Bereits existierende verhaltensweise Fragmente werden überschrieben." - }, - "deleteAllEntriesButton": { - "label": "Alle Fragmente löschen", - "tooltip": "Löscht alle Fragmente für diese Aufgabe.", - "question": "Bist du dir sicher, dass du alle Fragmente für diese Aufgabe löschen möchtest?", - "title": "Alle Fragmente löschen", - "success": "Alle Fragmente wurden erfolgreich gelöscht" - }, - "deleteIndividualEntryButton": { - "question": "Bist du dir sicher, dass du das Fragment mit ID {{ title }} löschen möchtest?" - } - }, - "step4": { - "name": "Code-Hinweise", - "description": "In diesem Schritt kannst du die Code-Hinweise generieren.", - "title": "Titel", - "task": "Aufgabe", - "notGenerated": "Bisher wurden keine Code-Hinweise generiert.", - "updateHintsButton": { - "label": "Code-Hinweise aktualisieren", - "tooltip": "Erstellt neue Code-Hinweise, indem die existierenden Lösungs-Codeschnipsel neu zugewiesen werden. Titel, Beschreibung und Inhalt bleiben unverändert beim ursprünglichen Hinweis.", - "success": "Code-Hinweise erfolgreich aktualisiert. Die Hinweise sind direkt sichtbar für teilnehmende Studierende." - }, - "createHintsButton": { - "label": "Code-Hinweise erstellen", - "tooltip": "Erstellt Code-Hinweise aus den existierenden Lösungs-Codeschnipseln.", - "success": "Code-Hinweise erfolgreich erstellt. Die Hinweise sind direkt sichtbar für teilnehmende Studierende." - }, - "recreateHintsButton": { - "label": "Code-Hinweise neu erstellen", - "tooltip": "Erstellt Code-Hinweise aus den existierenden Fragmenten. Existierende Hinweise werden überschrieben.", - "success": "Code-Hinweise erfolgreich neu erstellt. Die Hinweise sind direkt sichtbar für teilnehmende Studierende." - }, - "iris": { - "generateDescription": { - "label": "Beschreibungen generieren", - "tooltip": "Generiert eine kurze und lange Beschreibung basierend auf dem Lösungs-Codeschnipseln mithilfe von Iris." - } - } - } - } - }, - "programmingExerciseSolutionEntry": { - "simplifiedName": "Lösungs-Codeschnipsel", - "created": "Neues Lösungs-Codeschnipsel erstellt mit ID {{ param }}", - "updated": "Lösungs-Codeschnipsel aktualisiert mit ID {{ param }}", - "deleted": "Lösungs-Codeschnipsel gelöscht mit ID {{ param }}", - "test": "Test", - "testOnlyForCurrentTask": "Du kannst nur Tests auswählen, die in der aktuellen Task enthalten sind.", - "file": "Datei", - "changeContentNote": "Hinweis: Änderungen an diesem Fragment werden für Hinweise mit diesem Fragment automatisch übernommen." - } - } -} diff --git a/src/main/webapp/i18n/de/error.json b/src/main/webapp/i18n/de/error.json index c60e87ec42a3..7306229a03fb 100644 --- a/src/main/webapp/i18n/de/error.json +++ b/src/main/webapp/i18n/de/error.json @@ -83,7 +83,6 @@ }, "idNull": "Ungültige ID", "unexpectedError": "Ein unerwarteter Fehler ist aufgetreten: {{error}}", - "manualCodeHintOperation": "Dieser Endpunkt erlaubt keine manuelle Bearbeitung von Codehinweisen.", "exerciseNotDefined": "Die Aufgabe ist nicht definiert.", "exerciseIdMismatch": "Die Aufgaben-IDs stimmen nicht überein.", "solutionEntryError": "Fehler beim Laden der extrahierten Aufgaben aus der Aufgabenstellung.", diff --git a/src/main/webapp/i18n/de/iris.json b/src/main/webapp/i18n/de/iris.json index 95fa36c63909..2c01b1e3679d 100644 --- a/src/main/webapp/i18n/de/iris.json +++ b/src/main/webapp/i18n/de/iris.json @@ -31,7 +31,6 @@ "title": "Vorlesungen Erfassung Einstellungen", "autoIngestOnAttachmentUpload": "Vorlesungen automatisch an Pyris senden" }, - "hestiaSettings": "Hestia Einstellungen", "competencyGenerationSettings": "Kompetenzgenerierung Einstellungen", "proactivityBuildFailedEventEnabled": { "label": "Build-Fehler überwachen", @@ -70,8 +69,7 @@ "enabled": { "on": "Aktiviert", "off": "Deaktiviert", - "chat": "Iris Chat", - "hestia": "Hestia Integration" + "chat": "Iris Chat" }, "disabled": { "course": "IRIS ist in den Kurs-IRIS-Einstellungen deaktiviert", diff --git a/src/main/webapp/i18n/de/programmingExercise.json b/src/main/webapp/i18n/de/programmingExercise.json index 2fb203e24900..dbb1c466df22 100644 --- a/src/main/webapp/i18n/de/programmingExercise.json +++ b/src/main/webapp/i18n/de/programmingExercise.json @@ -218,21 +218,6 @@ "combineTemplateCommitsError": "Template-Repository-Commits konnten nicht zusammengeführt werden.", "diffReportError": "Der Diff Report konnte nicht erstellt werden.", "combineTemplateCommitsSuccess": "Template-Repository-Commits wurden zusammengeführt.", - "extractTasksFromProblemStatementWarning": "Nur zu Testzwecken: Extrahiere alle Aufgaben und Tests aus der Aufgabenstellung.", - "extractTasksFromProblemStatementSuccess": "{{numberTasks}} extrahierte Aufgaben mit {{numberTestCases}} Tests geladen:\n{{detailedResult}}", - "extractTasksFromProblemStatementTitle": "Lade extrahierte Aufgaben", - "deleteTasksAndSolutionEntriesSuccess": "Alle Aufgaben und Lösungs-Codeschnipsel wurden gelöscht.", - "deleteTasksAndSolutionEntriesWarning": "Nur zu Testzwecken: Lösche alle Aufgaben und Lösungs-Codeschnipsel.", - "deleteTasksAndSolutionEntriesTitle": "Lösche Aufgaben", - "generateCodeHintsTooltip": "Generiert Code Hinweise für alle Tasks dieser Aufgabe unter Verwendung der bereits erstellten Lösungs-Codeschnipsel", - "generateCodeHintsTitle": "Generiere Code Hinweise", - "generateCodeHintsSuccess": "Code Hinweise wurden erfolgreich generiert", - "createStructuralSolutionEntriesTooltip": "Erstellt Lösungs-Codeschnipsel für alle strukturellen Tests dieser Aufgabe.", - "createStructuralSolutionEntriesTitle": "Erstelle strukturelle Lösungs-Codeschnipsel", - "createStructuralSolutionEntriesSuccess": "Strukturelle Lösungs-Codeschnipsel wurden erfolgreich erstellt", - "createBehavioralSolutionEntriesTooltip": "Erstellt Lösungs-Codeschnipsel für alle Verhaltenstests dieser Aufgabe.", - "createBehavioralSolutionEntriesTitle": "Erstelle verhaltensweise Lösungs-Codeschnipsel", - "createBehavioralSolutionEntriesSuccess": "Verhaltensweise Lösungs-Codeschnipsel wurden erfolgreich erstellt", "editable": { "unsaved": "Ungespeichert.", "unsavedTooltip": "Es gibt ungespeicherte Änderungen in der Aufgabenstellung.", @@ -706,20 +691,9 @@ }, "withDependencies": "Mit beispielhafter Abhängigkeit", "withDependenciesTooltip": "Fügt dem generierten Projekt eine externe Apache commons-lang-Abhängigkeit als Beispiel dafür hinzu, wie Maven-Abhängigkeiten mit Artemis-Aufgaben verwendet werden sollten.", - "recordTestwiseCoverage": "Testbasierte Code-Abdeckung aufzeichnen", - "recordTestwiseCoverageTooltip": "Durch Aktivierung dieser Option wird die testbasierte Code-Abdeckung für das Lösungs-Repository aufgezeichnet. Diese Option ist nur für Java/Kotlin-Aufgaben mit nicht-sequentiellen Testläufen verfügbar.", "customizeBuildPlansWithAeolusTooltip": "Aktiviere diese Option, um das Docker Image und das Build Skript anzupassen, das auf Artemis für jede Einreichung in einem Docker Container ausgeführt wird.", "customizeBuildPlansTooltip": "Aktiviere diese Option, um das Docker Image und das Build Skript anzupassen, das auf Artemis für jede Einreichung in einem Docker Container ausgeführt wird.", "coveredLineRatio": "Anteil Test-Abgedeckter Zeilen", - "testwiseCoverageReport": { - "button": "Testbasierte Code-Abdeckung anzeigen", - "tooltip": "Zeigt die testbasierte Code-Abdeckung für das Lösungs-Repository.", - "title": "Testbasierte Code-Abdeckung", - "404": "Testbasierte Code-Abdeckung wurde noch nicht generiert. Bitte pushe etwas in die Lösungs- oder Test-Repositories um den Report zu erstellen.", - "selectTests": "Wähle die Tests aus, für die die Test-abgedeckten Zeilen markiert werden:", - "filePath": "Datei-Pfad", - "coveredLines": "Abgedeckte Zeilen" - }, "buildLogStatistics": { "title": "Durchschnittliche Statistiken der Build-Logs", "numberOfBuilds": "# Builds", diff --git a/src/main/webapp/i18n/en/codeHint.json b/src/main/webapp/i18n/en/codeHint.json deleted file mode 100644 index e0c014bbc557..000000000000 --- a/src/main/webapp/i18n/en/codeHint.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "artemisApp": { - "codeHint": { - "type": "Code Hint", - "entry": "Entry", - "entryType": "Type (S/B)", - "lineOfCode": "1 line of code", - "linesOfCode": "{{ lines }} lines of code", - "entryTypeTooltip": "Defines whether this entry has been generated for a structural (S) or behavioral (B) test case.", - "entryAbbreviation": { - "structural": "S", - "behavioral": "B" - }, - "entries": "Entries", - "removeEntryQuestion": "Are you sure you want to remove this entry?", - "noEntries": "This code hint has no Solution Code Snippets.", - "deleted": "Deleted Code Hint with identifier {{ param }}", - "management": { - "title": "Code Hint Management", - "previousButton": { - "label": "Back" - }, - "nextButton": { - "label": "Next", - "tooltip": "The current step has to be performed to proceed." - }, - "step1": { - "name": "Git-Diff Report", - "description": "The git-diff report is created automatically for the template and solution repositories for every submission to either of the repositories.", - "notGenerated": "The Git-Diff report has not been generated yet." - }, - "step2": { - "name": "Coverage Report", - "description": "The coverage report is created automatically for every submission to the solution repository.", - "notGenerated": "The coverage report has not been generated yet." - }, - "step3": { - "name": "Solution Code Snippets", - "description": "This steps allows editing the individual Solution Code Snippets from which code hints are generated.", - "notGenerated": "No Solution Code Snippets have been generated yet.", - "createManualFragmentButton": { - "label": "Create manual fragment", - "tooltip": "Allows to create a manual Solution Code Snippets for a file and test case." - }, - "structuralEntriesButton": { - "label": "Generate structural snippets", - "tooltip": "Generates fragments for structural test cases. Already existing structural fragments will be overwritten." - }, - "behavioralEntriesButton": { - "label": "Generate behavioral snippets", - "tooltip": "Generates fragments for behavioral test cases. Already existing behavioral fragments will be overwritten." - }, - "deleteAllEntriesButton": { - "label": "Delete all snippets", - "tooltip": "Deletes all Solution Code Snippets for this exercise.", - "question": "Are you sure to delete all Solution Code Snippets for this exercise?", - "title": "Delete all Solution Code Snippets", - "success": "Successfully deleted all Solution Code Snippets" - }, - "deleteIndividualEntryButton": { - "question": "Are you sure to delete the Solution Code Snippets with ID {{ title }}?" - } - }, - "step4": { - "name": "Code Hints", - "description": "This step allows generating the code hints.", - "title": "Title", - "task": "Task", - "notGenerated": "No code hints have been generated yet.", - "updateHintsButton": { - "label": "Update code hints", - "tooltip": "Create new code hints by re-assigning the existing Solution Code Snippets. The title, description, and content of the existing hint retain.", - "success": "Code hints have been updated successfully. The hints will be visible immediately to participating students." - }, - "createHintsButton": { - "label": "Create code hints", - "tooltip": "Creates code hints from the existing Solution Code Snippets.", - "success": "Code hints have been created successfully. The hints will be visible immediately to participating students." - }, - "recreateHintsButton": { - "label": "Recreate code hints", - "tooltip": "Recreate code hints from the existing Solution Code Snippets. Existing hints will be overwritten.", - "success": "Code hints have been recreated successfully. The hints will be visible immediately to participating students." - }, - "iris": { - "generateDescription": { - "label": "Generate description & content", - "tooltip": "Generates a short and long description and for the code hint based on the Solution Code Snippets using Iris." - } - } - } - } - }, - "programmingExerciseSolutionEntry": { - "simplifiedName": "Solution Code Snippets", - "created": "Created Solution Code Snippets with ID {{ param }}", - "updated": "Updated Solution Code Snippets with ID {{ param }}", - "deleted": "Deleted Solution Code Snippets with ID {{ param }}", - "test": "Test", - "testOnlyForCurrentTask": "You can only select a test case that the currently selected task contains.", - "file": "File", - "changeContentNote": "Note: Changes apply to the fragments in existing code hints that contain this fragment." - } - } -} diff --git a/src/main/webapp/i18n/en/error.json b/src/main/webapp/i18n/en/error.json index 900d1332c417..67386b7c53b4 100644 --- a/src/main/webapp/i18n/en/error.json +++ b/src/main/webapp/i18n/en/error.json @@ -83,7 +83,6 @@ }, "idNull": "Invalid ID", "unexpectedError": "An unexpected error occurred: {{error}}", - "manualCodeHintOperation": "This endpoint does not allow changing code hints manually.", "exerciseNotDefined": "The exercise is not defined.", "exerciseIdMismatch": "The exercise IDs do not match.", "solutionEntryError": "An error occurred while retrieving the extracted tasks from the problem statement.", diff --git a/src/main/webapp/i18n/en/iris.json b/src/main/webapp/i18n/en/iris.json index c36553db40fa..fd12226bb148 100644 --- a/src/main/webapp/i18n/en/iris.json +++ b/src/main/webapp/i18n/en/iris.json @@ -31,7 +31,6 @@ "title": "Lecture Ingestion Settings", "autoIngestOnAttachmentUpload": "Send Lectures To Pyris Automatically" }, - "hestiaSettings": "Hestia Settings", "competencyGenerationSettings": "Competency Generation Settings", "proactivityBuildFailedEventEnabled": { "label": "Monitor submission build failures", @@ -70,8 +69,7 @@ "enabled": { "on": "Enabled", "off": "Disabled", - "chat": "Iris Chat", - "hestia": "Hestia Integration" + "chat": "Iris Chat" }, "disabled": { "course": "IRIS is disabled in course IRIS settings", diff --git a/src/main/webapp/i18n/en/programmingExercise.json b/src/main/webapp/i18n/en/programmingExercise.json index e1340d958a4c..4982bf654361 100644 --- a/src/main/webapp/i18n/en/programmingExercise.json +++ b/src/main/webapp/i18n/en/programmingExercise.json @@ -218,21 +218,6 @@ "combineTemplateCommitsError": "Template repository commits could not be combined.", "diffReportError": "Diff report could not be generated.", "combineTemplateCommitsSuccess": "Template repository commits have been successfully combined.", - "extractTasksFromProblemStatementWarning": "Only for testing purpose: Extract all tasks and test cases from problems statement.", - "extractTasksFromProblemStatementSuccess": "Loaded {{numberTasks}} extracted tasks with {{numberTestCases}} tests from the problem statement:\n{{detailedResult}}", - "extractTasksFromProblemStatementTitle": "Get Extracted Tasks", - "deleteTasksAndSolutionEntriesWarning": "Only for testing purpose: Delete all tasks and Solution Code Snippets.", - "deleteTasksAndSolutionEntriesSuccess": "Successfully deleted all tasks and Solution Code Snippets.", - "deleteTasksAndSolutionEntriesTitle": "Delete Tasks", - "generateCodeHintsTooltip": "Generates code hints for all tasks in this exercise using the already created Solution Code Snippets.", - "generateCodeHintsTitle": "Generate code hints", - "generateCodeHintsSuccess": "Code hints have been generated", - "createStructuralSolutionEntriesTooltip": "Creates Solution Code Snippets for all structural test cases of this exercise.", - "createStructuralSolutionEntriesTitle": "Create structural Solution Code Snippets", - "createStructuralSolutionEntriesSuccess": "Structural Solution Code Snippets have been created successfully", - "createBehavioralSolutionEntriesTooltip": "Creates Solution Code Snippets for all behavioral test cases of this exercise.", - "createBehavioralSolutionEntriesTitle": "Create behavioral Solution Code Snippets", - "createBehavioralSolutionEntriesSuccess": "Behavioral Solution Code Snippets have been created successfully", "editable": { "unsaved": "Unsaved.", "unsavedTooltip": "There are unsaved changes in the problem statement.", @@ -705,20 +690,9 @@ }, "withDependencies": "With exemplary dependency", "withDependenciesTooltip": "Adds an external Apache commons-lang dependency to the generated project as an example how Maven dependencies should be used with Artemis exercises.", - "recordTestwiseCoverage": "Record Testwise Coverage", - "recordTestwiseCoverageTooltip": "Activate this option to record the testwise coverage for the solution repository. This option is only available for Java/Kotlin-exercises with non-sequential test runs.", "customizeBuildPlansWithAeolusTooltip": "Activate this option to customize the docker image and build plan which is executed on Artemis for each submission in a Docker container.", "customizeBuildPlansTooltip": "Activate this option to customize the docker image and build plan which is executed on Artemis for each submission in a Docker container.", "coveredLineRatio": "Ratio Test-Covered Lines", - "testwiseCoverageReport": { - "button": "Show testwise coverage", - "tooltip": "Shows the testwise coverage for the solution repository.", - "title": "Testwise Coverage", - "404": "Testwise Coverage has not been generated yet. Please do a push to the solution or test repository to generate the report.", - "selectTests": "Select the tests for which the covered lines are marked:", - "filePath": "File Path", - "coveredLines": "Covered Lines" - }, "buildLogStatistics": { "title": "Average build log statistics", "numberOfBuilds": "# Builds", diff --git a/src/test/java/de/tum/cit/aet/artemis/buildagent/service/BuildAgentDockerServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/buildagent/service/BuildAgentDockerServiceTest.java index c0c289c01bb8..cdc2af3ec1b1 100644 --- a/src/test/java/de/tum/cit/aet/artemis/buildagent/service/BuildAgentDockerServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/buildagent/service/BuildAgentDockerServiceTest.java @@ -92,8 +92,7 @@ void testPullDockerImage() { InspectImageCmd inspectImageCmd = mock(InspectImageCmd.class); doReturn(inspectImageCmd).when(dockerClient).inspectImageCmd(anyString()); doThrow(new NotFoundException("")).when(inspectImageCmd).exec(); - BuildConfig buildConfig = new BuildConfig("echo 'test'", "test-image-name", "test", "test", "test", "test", null, null, false, false, false, null, 0, null, null, null, - null); + BuildConfig buildConfig = new BuildConfig("echo 'test'", "test-image-name", "test", "test", "test", "test", null, null, false, false, null, 0, null, null, null, null); BuildAgentDTO buildAgent = new BuildAgentDTO("buildagent1", "address1", "buildagent1"); var build = new BuildJobQueueItem("1", "job1", buildAgent, 1, 1, 1, 1, 1, BuildStatus.SUCCESSFUL, null, null, buildConfig, null); // Pull image diff --git a/src/test/java/de/tum/cit/aet/artemis/buildagent/service/BuildResultTest.java b/src/test/java/de/tum/cit/aet/artemis/buildagent/service/BuildResultTest.java index b7ad99348575..32c64c25d0fe 100644 --- a/src/test/java/de/tum/cit/aet/artemis/buildagent/service/BuildResultTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/buildagent/service/BuildResultTest.java @@ -11,9 +11,7 @@ class BuildResultTest extends AbstractSpringIntegrationLocalCILocalVCTest { @Test void testUnsupportedMethods() { - BuildResult buildResult = new BuildResult(null, null, null, true, null, null, null); - + BuildResult buildResult = new BuildResult(null, null, null, true, null, null, null, null, false); assertThat(buildResult.extractBuildLogs()).isEmpty(); - assertThat(buildResult.getTestwiseCoverageReports()).isEmpty(); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/buildagent/service/TestResultXmlParserTest.java b/src/test/java/de/tum/cit/aet/artemis/buildagent/service/TestResultXmlParserTest.java index 0a8931701c04..458dfc011191 100644 --- a/src/test/java/de/tum/cit/aet/artemis/buildagent/service/TestResultXmlParserTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/buildagent/service/TestResultXmlParserTest.java @@ -8,13 +8,13 @@ import org.junit.jupiter.api.Test; -import de.tum.cit.aet.artemis.buildagent.dto.BuildResult; +import de.tum.cit.aet.artemis.buildagent.dto.LocalCITestJobDTO; class TestResultXmlParserTest { - private final List failedTests = new ArrayList<>(); + private final List failedTests = new ArrayList<>(); - private final List successfulTests = new ArrayList<>(); + private final List successfulTests = new ArrayList<>(); @Test void testParseResultXmlInnerText() throws IOException { @@ -31,8 +31,8 @@ void testParseResultXmlInnerText() throws IOException { TestResultXmlParser.processTestResultFile(exampleXml, failedTests, successfulTests); assertThat(failedTests).hasSize(1); var test = failedTests.getFirst(); - assertThat(test.getName()).isEqualTo("testBubbleSort()"); - assertThat(test.getTestMessages()).containsExactly(""" + assertThat(test.name()).isEqualTo("testBubbleSort()"); + assertThat(test.testMessages()).containsExactly(""" test `add` failed on ≥ 1 cases: (0, 0) Your submission raised an error Failure("TODO add")"""); @@ -52,8 +52,8 @@ void testParseResultXmlMessageAttribute() throws IOException { TestResultXmlParser.processTestResultFile(exampleXml, failedTests, successfulTests); assertThat(failedTests).hasSize(1); var test = failedTests.getFirst(); - assertThat(test.getName()).isEqualTo("testBubbleSort()"); - assertThat(test.getTestMessages()).containsExactly("test `add` failed"); + assertThat(test.name()).isEqualTo("testBubbleSort()"); + assertThat(test.testMessages()).containsExactly("test `add` failed"); } @Test @@ -69,8 +69,8 @@ void testParseResultXmlCData() throws IOException { TestResultXmlParser.processTestResultFile(exampleXml, failedTests, successfulTests); assertThat(failedTests).hasSize(1); var test = failedTests.getFirst(); - assertThat(test.getName()).isEqualTo("testMergeSort()"); - assertThat(failedTests.getFirst().getTestMessages()).containsExactly("org.opentest4j.AssertionFailedError: Deine Einreichung enthält keine Ausgabe. (67cac2)"); + assertThat(test.name()).isEqualTo("testMergeSort()"); + assertThat(failedTests.getFirst().testMessages()).containsExactly("org.opentest4j.AssertionFailedError: Deine Einreichung enthält keine Ausgabe. (67cac2)"); } @Test @@ -88,7 +88,7 @@ void testSuccessfulTests() throws IOException { TestResultXmlParser.processTestResultFile(exampleXml, failedTests, successfulTests); assertThat(failedTests).isEmpty(); assertThat(successfulTests).hasSize(4); - assertThat(successfulTests).map(BuildResult.LocalCITestJobDTO::getName).containsExactlyInAnyOrder("testMergeSort()", "testUseBubbleSortForSmallList()", "testBubbleSort()", + assertThat(successfulTests).map(LocalCITestJobDTO::name).containsExactlyInAnyOrder("testMergeSort()", "testUseBubbleSortForSmallList()", "testBubbleSort()", "testUseMergeSortForBigList()"); } @@ -184,8 +184,8 @@ void testOutputInvalidXMLCharacters() throws IOException { assertThat(successfulTests).isEmpty(); assertThat(failedTests).hasSize(1); var test = failedTests.getFirst(); - assertThat(test.getName()).isEqualTo("CompileLinkedList"); - assertThat(test.getTestMessages().getFirst()).isEqualTo("Build for directory ../assignment/build failed. Returncode is 2."); + assertThat(test.name()).isEqualTo("CompileLinkedList"); + assertThat(test.testMessages().getFirst()).isEqualTo("Build for directory ../assignment/build failed. Returncode is 2."); } @Test @@ -205,8 +205,8 @@ void testEmptyTestMessage() throws IOException { TestResultXmlParser.processTestResultFile(input, failedTests, successfulTests); assertThat(failedTests).hasSize(1); var test = failedTests.getFirst(); - assertThat(test.getName()).isEqualTo("mwe-name"); - assertThat(test.getTestMessages()).hasSize(1).contains(""); + assertThat(test.name()).isEqualTo("mwe-name"); + assertThat(test.testMessages()).hasSize(1).contains(""); } @Test @@ -242,7 +242,7 @@ void testNestedTestsuite() throws IOException { TestResultXmlParser.processTestResultFile(input, failedTests, successfulTests); // @formatter:off - assertThat(successfulTests).extracting(BuildResult.LocalCITestJobDTO::getName).containsExactlyInAnyOrder( + assertThat(successfulTests).extracting(LocalCITestJobDTO::name).containsExactlyInAnyOrder( "Properties.Checked by SmallCheck.Testing filtering in A", "Properties.Checked by SmallCheck.Testing mapping in A", "Properties.Checked by SmallCheck.Testing filtering in B", @@ -280,7 +280,7 @@ void testMultipleTestsuite() throws IOException { TestResultXmlParser.processTestResultFile(input, failedTests, successfulTests); // @formatter:off - assertThat(successfulTests).extracting(BuildResult.LocalCITestJobDTO::getName).containsExactlyInAnyOrder( + assertThat(successfulTests).extracting(LocalCITestJobDTO::name).containsExactlyInAnyOrder( "SuiteA.Test1", "SuiteA.Test2", "SuiteA.Test3", @@ -310,7 +310,7 @@ void testNestedTestsuiteMissingNames() throws IOException { TestResultXmlParser.processTestResultFile(input, failedTests, successfulTests); - assertThat(successfulTests).extracting(BuildResult.LocalCITestJobDTO::getName).containsExactlyInAnyOrder("Test1", "Test2", "Test3"); + assertThat(successfulTests).extracting(LocalCITestJobDTO::name).containsExactlyInAnyOrder("Test1", "Test2", "Test3"); assertThat(failedTests).isEmpty(); } @@ -329,7 +329,7 @@ void testXmlProlog() throws IOException { TestResultXmlParser.processTestResultFile(input, failedTests, successfulTests); - assertThat(successfulTests).singleElement().extracting(BuildResult.LocalCITestJobDTO::getName).isEqualTo("Test"); + assertThat(successfulTests).singleElement().extracting(LocalCITestJobDTO::name).isEqualTo("Test"); assertThat(failedTests).isEmpty(); } @@ -346,7 +346,7 @@ void testRootTestsuiteNameIgnored() throws IOException { TestResultXmlParser.processTestResultFile(input, failedTests, successfulTests); - assertThat(successfulTests).singleElement().extracting(BuildResult.LocalCITestJobDTO::getName).isEqualTo("Suite.Test"); + assertThat(successfulTests).singleElement().extracting(LocalCITestJobDTO::name).isEqualTo("Suite.Test"); assertThat(failedTests).isEmpty(); } @@ -365,7 +365,7 @@ void testSingleTopLevelTestsuiteNameIgnored() throws IOException { TestResultXmlParser.processTestResultFile(input, failedTests, successfulTests); - assertThat(successfulTests).singleElement().extracting(BuildResult.LocalCITestJobDTO::getName).isEqualTo("Suite.Test"); + assertThat(successfulTests).singleElement().extracting(LocalCITestJobDTO::name).isEqualTo("Suite.Test"); assertThat(failedTests).isEmpty(); } @@ -385,7 +385,7 @@ void testMixedNestedTestsuiteTestcase() throws IOException { TestResultXmlParser.processTestResultFile(input, failedTests, successfulTests); - assertThat(successfulTests).extracting(BuildResult.LocalCITestJobDTO::getName).containsExactlyInAnyOrder("Test", "Suite.Test"); + assertThat(successfulTests).extracting(LocalCITestJobDTO::name).containsExactlyInAnyOrder("Test", "Suite.Test"); assertThat(failedTests).isEmpty(); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/core/service/TitleCacheEvictionServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/core/service/TitleCacheEvictionServiceTest.java index 30f880543785..9f969fb29328 100644 --- a/src/test/java/de/tum/cit/aet/artemis/core/service/TitleCacheEvictionServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/core/service/TitleCacheEvictionServiceTest.java @@ -20,8 +20,6 @@ import de.tum.cit.aet.artemis.modeling.domain.DiagramType; import de.tum.cit.aet.artemis.modeling.repository.ApollonDiagramRepository; import de.tum.cit.aet.artemis.modeling.util.ModelingExerciseFactory; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; import de.tum.cit.aet.artemis.text.util.TextExerciseUtilService; @@ -50,9 +48,6 @@ class TitleCacheEvictionServiceTest extends AbstractSpringIntegrationIndependent @Autowired private ExamRepository examRepository; - @Autowired - private ExerciseHintRepository exerciseHintRepository; - @Autowired private CourseUtilService courseUtilService; @@ -211,40 +206,6 @@ void testEvictsTitleOnUpdateTitleOrDeleteExam() { })); } - @Test - void testEvictsTitleOnUpdateTitleOrDeleteExerciseHint() { - var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(); - var exercise = (ProgrammingExercise) course.getExercises().stream().findAny().orElseThrow(); - programmingExerciseUtilService.addHintsToExercise(exercise); - var hint = exercise.getExerciseHints().stream().findFirst().orElseThrow(); - testCacheEvicted("exerciseHintTitle", () -> new Tuple<>(exercise.getId() + "-" + hint.getId(), hint.getTitle()), List.of( - // Should evict as we change the title - () -> { - hint.setTitle("testEvictsTitleOnUpdateTitleOrDeleteExerciseHint"); - exerciseHintRepository.save(hint); - return true; - }, - // Should not evict as title remains the same - () -> { - hint.setDescription("testEvictsTitleOnUpdateTitleOrDeleteExerciseHint"); // Change some other values - exerciseHintRepository.save(hint); - return false; - }, - // Should not do something if the exercise is missing - () -> { - hint.setExercise(null); - hint.setTitle("testEvictsTitleOnUpdateTitleOrDeleteExerciseHint"); - exerciseHintRepository.save(hint); - return false; - }, - // Should evict after deletion - () -> { - hint.setExercise(exercise); - exerciseHintRepository.delete(hint); - return true; - })); - } - private void testCacheEvicted(String cacheName, Supplier> idTitleSupplier, List> entityModifiers) { var cache = cacheManager.getCache(cacheName); assertThat(cache).isNotNull(); diff --git a/src/test/java/de/tum/cit/aet/artemis/core/util/CourseTestService.java b/src/test/java/de/tum/cit/aet/artemis/core/util/CourseTestService.java index 690a781a5848..82696055a742 100644 --- a/src/test/java/de/tum/cit/aet/artemis/core/util/CourseTestService.java +++ b/src/test/java/de/tum/cit/aet/artemis/core/util/CourseTestService.java @@ -2488,7 +2488,7 @@ public void testCleanupCourseAsInstructor_no_Archive() throws Exception { // Test public void testCleanupCourseAsInstructor() throws Exception { // Generate a course that has an archive - var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, false, ProgrammingLanguage.JAVA); + var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, ProgrammingLanguage.JAVA); course.setCourseArchivePath("some-archive-path"); course = courseRepo.save(course); diff --git a/src/test/java/de/tum/cit/aet/artemis/plagiarism/PlagiarismDetectionServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/plagiarism/PlagiarismDetectionServiceTest.java index 2c2f971a37a9..560a74a893c7 100644 --- a/src/test/java/de/tum/cit/aet/artemis/plagiarism/PlagiarismDetectionServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/plagiarism/PlagiarismDetectionServiceTest.java @@ -93,7 +93,7 @@ void shouldExecuteChecksForProgrammingExercise() throws IOException, ExitExcepti .thenReturn(programmingPlagiarismResult); // and - var programmingLanguageFeature = new ProgrammingLanguageFeature(null, false, false, true, false, false, emptyList(), false, false); + var programmingLanguageFeature = new ProgrammingLanguageFeature(null, false, false, true, false, false, emptyList(), false); when(programmingLanguageFeatureService.getProgrammingLanguageFeatures(any())).thenReturn(programmingLanguageFeature); // when @@ -107,7 +107,7 @@ void shouldExecuteChecksForProgrammingExercise() throws IOException, ExitExcepti void shouldThrowExceptionOnUnsupportedProgrammingLanguage() { // given var programmingExercise = new ProgrammingExercise(); - var programmingLanguageFeature = new ProgrammingLanguageFeature(null, false, false, false, false, false, emptyList(), false, false); + var programmingLanguageFeature = new ProgrammingLanguageFeature(null, false, false, false, false, false, emptyList(), false); when(programmingLanguageFeatureService.getProgrammingLanguageFeatures(any())).thenReturn(programmingLanguageFeature); // expect @@ -125,7 +125,7 @@ void shouldExecuteChecksWithJplagReportForProgrammingExercise() throws Programmi .thenReturn(zipFile); // and - var programmingLanguageFeature = new ProgrammingLanguageFeature(null, false, false, true, false, false, emptyList(), false, false); + var programmingLanguageFeature = new ProgrammingLanguageFeature(null, false, false, true, false, false, emptyList(), false); when(programmingLanguageFeatureService.getProgrammingLanguageFeatures(any())).thenReturn(programmingLanguageFeature); // when @@ -139,7 +139,7 @@ void shouldExecuteChecksWithJplagReportForProgrammingExercise() throws Programmi void shouldThrowExceptionOnUnsupportedProgrammingLanguageForChecksWithJplagReport() { // given var programmingExercise = new ProgrammingExercise(); - var programmingLanguageFeature = new ProgrammingLanguageFeature(null, false, false, false, false, false, emptyList(), false, false); + var programmingLanguageFeature = new ProgrammingLanguageFeature(null, false, false, false, false, false, emptyList(), false); when(programmingLanguageFeatureService.getProgrammingLanguageFeatures(any())).thenReturn(programmingLanguageFeature); // expect diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationIndependentTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationIndependentTest.java index 21791e5d24f7..3bacbd850f01 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationIndependentTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationIndependentTest.java @@ -19,13 +19,6 @@ import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; import de.tum.cit.aet.artemis.programming.repository.StaticCodeAnalysisCategoryRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CodeHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageFileReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintActivationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.TestwiseCoverageReportEntryRepository; import de.tum.cit.aet.artemis.programming.repository.settings.IdeRepository; import de.tum.cit.aet.artemis.programming.repository.settings.UserIdeMappingRepository; import de.tum.cit.aet.artemis.programming.service.AuxiliaryRepositoryService; @@ -33,9 +26,7 @@ import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseFeedbackCreationService; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseGradingService; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseRepositoryService; -import de.tum.cit.aet.artemis.programming.service.hestia.CodeHintService; -import de.tum.cit.aet.artemis.programming.service.hestia.ExerciseHintService; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseTaskService; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseStudentParticipationTestRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTaskTestRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; @@ -51,30 +42,12 @@ public abstract class AbstractProgrammingIntegrationIndependentTest extends Abst @Autowired protected AuxiliaryRepositoryRepository auxiliaryRepositoryRepository; - @Autowired - protected CodeHintRepository codeHintRepository; - - @Autowired - protected CoverageFileReportRepository coverageFileReportRepository; - - @Autowired - protected CoverageReportRepository coverageReportRepository; - - @Autowired - protected ExerciseHintActivationRepository exerciseHintActivationRepository; - - @Autowired - protected ExerciseHintRepository exerciseHintRepository; - @Autowired protected IdeRepository ideRepository; @Autowired protected ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; - @Autowired - protected ProgrammingExerciseSolutionEntryRepository programmingExerciseSolutionEntryRepository; - @Autowired protected ProgrammingExerciseStudentParticipationTestRepository programmingExerciseStudentParticipationRepository; @@ -96,9 +69,6 @@ public abstract class AbstractProgrammingIntegrationIndependentTest extends Abst @Autowired protected StaticCodeAnalysisCategoryRepository staticCodeAnalysisCategoryRepository; - @Autowired - protected TestwiseCoverageReportEntryRepository testwiseCoverageReportEntryRepository; - @Autowired protected UserIdeMappingRepository userIdeMappingRepository; @@ -131,12 +101,6 @@ public abstract class AbstractProgrammingIntegrationIndependentTest extends Abst @Autowired protected BuildLogEntryService buildLogEntryService; - @Autowired - protected CodeHintService codeHintService; - - @Autowired - protected ExerciseHintService exerciseHintService; - @Autowired protected GitUtilService gitUtilService; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTestBase.java b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTestBase.java index 56a168f7e776..5112f51af58a 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTestBase.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTestBase.java @@ -25,19 +25,11 @@ import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.SolutionProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.TemplateProgrammingExerciseParticipation; -import de.tum.cit.aet.artemis.programming.hestia.util.HestiaUtilTestService; import de.tum.cit.aet.artemis.programming.repository.AuxiliaryRepositoryRepository; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseGitDiffReportRepository; import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageFileReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseGitDiffReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.TestwiseCoverageReportEntryRepository; import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; import de.tum.cit.aet.artemis.programming.service.ParticipationVcsAccessTokenService; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseGitDiffReportService; -import de.tum.cit.aet.artemis.programming.service.hestia.TestwiseCoverageService; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralTestCaseService; -import de.tum.cit.aet.artemis.programming.service.hestia.structural.StructuralTestCaseService; import de.tum.cit.aet.artemis.programming.service.localci.LocalCIResultService; import de.tum.cit.aet.artemis.programming.service.localci.LocalCITriggerService; import de.tum.cit.aet.artemis.programming.service.localvc.LocalVCServletService; @@ -67,12 +59,6 @@ public abstract class AbstractProgrammingIntegrationLocalCILocalVCTestBase exten @Autowired protected AuxiliaryRepositoryRepository auxiliaryRepositoryRepository; - @Autowired - protected CoverageFileReportRepository coverageFileReportRepository; - - @Autowired - protected CoverageReportRepository coverageReportRepository; - @Autowired protected ProgrammingExerciseGitDiffReportRepository reportRepository; @@ -85,9 +71,6 @@ public abstract class AbstractProgrammingIntegrationLocalCILocalVCTestBase exten @Autowired protected SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseRepository; - @Autowired - protected TestwiseCoverageReportEntryRepository testwiseCoverageReportEntryRepository; - // External Repositories @Autowired protected ExamRepository examRepository; @@ -99,15 +82,9 @@ public abstract class AbstractProgrammingIntegrationLocalCILocalVCTestBase exten protected TeamRepository teamRepository; // Services - @Autowired - protected BehavioralTestCaseService behavioralTestCaseService; - @Autowired protected BuildLogEntryService buildLogEntryService; - @Autowired - protected HestiaUtilTestService hestiaUtilTestService; - @Autowired protected LocalCIResultService localCIResultService; @@ -120,15 +97,6 @@ public abstract class AbstractProgrammingIntegrationLocalCILocalVCTestBase exten @Autowired protected ParticipationVcsAccessTokenService participationVcsAccessTokenService; - @Autowired - protected ProgrammingExerciseGitDiffReportService reportService; - - @Autowired - protected StructuralTestCaseService structuralTestCaseService; - - @Autowired - protected TestwiseCoverageService testwiseCoverageService; - // External Services // Util Services diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/BuildPlanIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/BuildPlanIntegrationTest.java index 82809c5875e2..e62a95dd68aa 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/BuildPlanIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/BuildPlanIntegrationTest.java @@ -31,7 +31,6 @@ void init() { programmingExercise.setProjectType(ProjectType.MAVEN_MAVEN); programmingExercise.setStaticCodeAnalysisEnabled(true); buildConfig.setSequentialTestRuns(false); - buildConfig.setTestwiseCoverageEnabled(false); var savedBuildConfig = programmingExerciseBuildConfigRepository.save(buildConfig); programmingExercise.setBuildConfig(savedBuildConfig); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationJenkinsGitlabTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationJenkinsGitlabTest.java index f2b10fd57caf..521e9f7784db 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationJenkinsGitlabTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationJenkinsGitlabTest.java @@ -234,7 +234,7 @@ void testGetProgrammingExerciseWithJustTemplateAndSolutionParticipation(boolean @ValueSource(booleans = { true, false }) @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") void testGetProgrammingExerciseWithTemplateAndSolutionParticipationAndAuxiliaryRepositories(boolean withSubmissionResults) throws Exception { - programmingExerciseIntegrationTestService.testGetProgrammingExerciseWithTemplateAndSolutionParticipationAndAuxiliaryRepositories(withSubmissionResults, false); + programmingExerciseIntegrationTestService.testGetProgrammingExerciseWithTemplateAndSolutionParticipationAndAuxiliaryRepositories(withSubmissionResults); } @Test @@ -345,12 +345,6 @@ void updateProgrammingExercise_updatingSCAEnabledOption_badRequest() throws Exce programmingExerciseIntegrationTestService.updateProgrammingExerciseShouldFailWithBadRequestWhenUpdatingSCAOption(); } - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void updateProgrammingExercise_updatingCoverageOption_badRequest() throws Exception { - programmingExerciseIntegrationTestService.updateProgrammingExerciseShouldFailWithBadRequestWhenUpdatingCoverageOption(); - } - @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void updateExerciseDueDateWithIndividualDueDateUpdate() throws Exception { @@ -612,21 +606,6 @@ void createProgrammingExercise_notIncluded_invalidBonusPoints_badRequest() throw programmingExerciseIntegrationTestService.createProgrammingExercise_notIncluded_invalidBonusPoints_badRequest(); } - private static Set generateSupportedLanguagesWithoutJavaAndKotlin() { - Set supportedLanguages = ArgumentSources.generateJenkinsSupportedLanguages(); - supportedLanguages.remove(ProgrammingLanguage.JAVA); - supportedLanguages.remove(ProgrammingLanguage.KOTLIN); - return supportedLanguages; - } - - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") - // It should return a bad request error for all ProgrammingExercises except Java and Kotlin. - @MethodSource("generateSupportedLanguagesWithoutJavaAndKotlin") - void createProgrammingExercise_testwiseCoverageAnalysisNotSupported_badRequest(ProgrammingLanguage programmingLanguage) throws Exception { - programmingExerciseIntegrationTestService.createProgrammingExercise_testwiseCoverageAnalysisNotSupported_badRequest(programmingLanguage); - } - @Test @WithMockUser(username = TEST_PREFIX + "instructoralt1", roles = "INSTRUCTOR") void importProgrammingExercise_sourceExerciseIdNegative_badRequest() throws Exception { diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java index b7885c881155..415101303e07 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java @@ -109,7 +109,6 @@ import de.tum.cit.aet.artemis.programming.dto.ProgrammingExerciseResetOptionsDTO; import de.tum.cit.aet.artemis.programming.dto.ProgrammingExerciseTestCaseDTO; import de.tum.cit.aet.artemis.programming.dto.ProgrammingExerciseTestCaseStateDTO; -import de.tum.cit.aet.artemis.programming.hestia.util.HestiaUtilTestService; import de.tum.cit.aet.artemis.programming.repository.AuxiliaryRepositoryRepository; import de.tum.cit.aet.artemis.programming.service.GitService; import de.tum.cit.aet.artemis.programming.service.UriService; @@ -124,11 +123,13 @@ import de.tum.cit.aet.artemis.programming.util.MockDelegate; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; +import de.tum.cit.aet.artemis.programming.util.ProgrammingUtilTestService; import de.tum.cit.aet.artemis.text.util.TextExerciseUtilService; /** * Note: this class should be independent of the actual VCS and CIS and contains common test logic for scenarios: * 1) Jenkins + Gitlab + * 2) LocalVC + LocalCI */ @Service public class ProgrammingExerciseIntegrationTestService { @@ -197,9 +198,6 @@ public class ProgrammingExerciseIntegrationTestService { @Autowired private TextExerciseUtilService textExerciseUtilService; - @Autowired - protected HestiaUtilTestService hestiaUtilTestService; - @Autowired private ProgrammingExerciseTestRepository programmingExerciseTestRepository; @@ -212,6 +210,9 @@ public class ProgrammingExerciseIntegrationTestService { @Autowired private GradingCriterionRepository gradingCriterionRepository; + @Autowired + private ProgrammingUtilTestService programmingUtilTestService; + private Course course; public ProgrammingExercise programmingExercise; @@ -579,19 +580,19 @@ void testExportSubmissionsByParticipationIds_instructorNotInCourse_forbidden() t } void testExportSubmissionsByStudentLogins() throws Exception { - File downloadedFile = exportSubmissionsByStudentLogins(HttpStatus.OK); + File downloadedFile = exportSubmissionsByStudentLogins(); assertThat(downloadedFile).exists(); // TODO: unzip the files and add some checks } - private File exportSubmissionsByStudentLogins(HttpStatus expectedStatus) throws Exception { + private File exportSubmissionsByStudentLogins() throws Exception { var repository1 = gitService.getExistingCheckedOutRepositoryByLocalPath(localRepoFile.toPath(), null); var repository2 = gitService.getExistingCheckedOutRepositoryByLocalPath(localRepoFile2.toPath(), null); doReturn(repository1).when(gitService).getOrCheckoutRepository(eq(participation1.getVcsRepositoryUri()), anyString(), anyBoolean()); doReturn(repository2).when(gitService).getOrCheckoutRepository(eq(participation2.getVcsRepositoryUri()), anyString(), anyBoolean()); final var path = "/api/programming-exercises/" + programmingExercise.getId() + "/export-repos-by-participant-identifiers/" + userPrefix + "student1," + userPrefix + "student2"; - return request.postWithResponseBodyFile(path, getOptions(), expectedStatus); + return request.postWithResponseBodyFile(path, getOptions(), HttpStatus.OK); } private RepositoryExportOptionsDTO getOptions() { @@ -763,24 +764,19 @@ void testGetProgrammingExerciseWithJustTemplateAndSolutionParticipation(boolean assertThat(programmingExerciseServer.getStudentParticipations()).isEmpty(); } - void testGetProgrammingExerciseWithTemplateAndSolutionParticipationAndAuxiliaryRepositories(boolean withSubmissionResults, boolean withGradingCriteria) throws Exception { + void testGetProgrammingExerciseWithTemplateAndSolutionParticipationAndAuxiliaryRepositories(boolean withSubmissionResults) throws Exception { AuxiliaryRepository auxiliaryRepository = programmingExerciseUtilService.addAuxiliaryRepositoryToExercise(programmingExercise); Set gradingCriteria = exerciseUtilService.addGradingInstructionsToExercise(programmingExercise); - gradingCriteria = Set.copyOf(gradingCriterionRepository.saveAll(gradingCriteria)); + gradingCriterionRepository.saveAll(gradingCriteria); programmingExercise = programmingExerciseRepository.save(programmingExercise); var path = "/api/programming-exercises/" + programmingExercise.getId() + "/with-template-and-solution-participation" + "?withSubmissionResults=" + withSubmissionResults - + "&withGradingCriteria=" + withGradingCriteria; + + "&withGradingCriteria=" + false; var programmingExerciseServer = request.get(path, HttpStatus.OK, ProgrammingExercise.class); checkTemplateAndSolutionParticipationsFromServer(programmingExerciseServer); assertThat(programmingExerciseServer.getAuxiliaryRepositories()).hasSize(1).containsExactly(auxiliaryRepository); - if (withGradingCriteria) { - assertThat(programmingExerciseServer.getGradingCriteria()).containsAll(gradingCriteria); - } - else { - assertThat(programmingExerciseServer.getGradingCriteria()).isEmpty(); - } + assertThat(programmingExerciseServer.getGradingCriteria()).isEmpty(); } private void checkTemplateAndSolutionParticipationsFromServer(ProgrammingExercise programmingExerciseServer) { @@ -962,18 +958,6 @@ void updateProgrammingExerciseShouldFailWithBadRequestWhenUpdatingSCAOption() th request.put("/api/programming-exercises", updatedExercise, HttpStatus.BAD_REQUEST); } - /** - * This test checks that it is not allowed to change coverage enabled option - */ - void updateProgrammingExerciseShouldFailWithBadRequestWhenUpdatingCoverageOption() throws Exception { - mockBuildPlanAndRepositoryCheck(programmingExercise); - - ProgrammingExercise updatedExercise = programmingExercise; - updatedExercise.getBuildConfig().setTestwiseCoverageEnabled(true); - - request.put("/api/programming-exercises", updatedExercise, HttpStatus.BAD_REQUEST); - } - void updateExerciseDueDateWithIndividualDueDateUpdate() throws Exception { mockBuildPlanAndRepositoryCheck(programmingExercise); mockConfigureRepository(programmingExercise); @@ -988,6 +972,7 @@ void updateExerciseDueDateWithIndividualDueDateUpdate() throws Exception { } programmingExercise.setDueDate(ZonedDateTime.now().plusHours(12)); + assertThat(programmingExercise.getDueDate()).isNotNull(); programmingExercise.setReleaseDate(programmingExercise.getDueDate().minusDays(1)); request.put("/api/programming-exercises", programmingExercise, HttpStatus.OK); @@ -1308,16 +1293,6 @@ void createProgrammingExercise_notIncluded_invalidBonusPoints_badRequest() throw request.post("/api/programming-exercises/setup", programmingExercise, HttpStatus.BAD_REQUEST); } - void createProgrammingExercise_testwiseCoverageAnalysisNotSupported_badRequest(ProgrammingLanguage programmingLanguage) throws Exception { - programmingExercise.setId(null); - programmingExercise.setProjectType(null); - programmingExercise.setTitle("New title"); - programmingExercise.setShortName("NewShortname"); - programmingExercise.setProgrammingLanguage(programmingLanguage); - programmingExercise.getBuildConfig().setTestwiseCoverageEnabled(true); - request.post("/api/programming-exercises/setup", programmingExercise, HttpStatus.BAD_REQUEST); - } - void importProgrammingExercise_sourceExerciseIdNegative_badRequest() throws Exception { programmingExercise.setId(-1L); request.post("/api/programming-exercises/import/" + programmingExercise.getId(), programmingExercise, HttpStatus.BAD_REQUEST); @@ -1544,7 +1519,7 @@ void updateTestCases_asInstrutor() throws Exception { testCasesResponse.forEach(testCase -> testCase.setExercise(programmingExercise)); final var testCasesInDB = programmingExerciseTestCaseRepository.findByExerciseId(programmingExercise.getId()); - assertThat(new HashSet<>(testCasesResponse)).usingRecursiveFieldByFieldElementComparatorIgnoringFields("exercise", "tasks", "solutionEntries", "coverageEntries") + assertThat(new HashSet<>(testCasesResponse)).usingRecursiveFieldByFieldElementComparatorIgnoringFields("exercise", "tasks") .containsExactlyInAnyOrderElementsOf(testCasesInDB); assertThat(testCasesResponse).allSatisfy(testCase -> { assertThat(testCase.isAfterDueDate()).isTrue(); @@ -2286,34 +2261,11 @@ void testReEvaluateAndUpdateProgrammingExercise_isNotSameGivenExerciseIdInReques request.put("/api/programming-exercises/" + programmingExercise.getId() + "/re-evaluate", programmingExerciseToBeConflicted, HttpStatus.CONFLICT); } - void test_redirectGetSolutionRepositoryFilesWithoutContent() throws Exception { - test_redirectGetSolutionRepositoryFilesWithoutContent((exercise, files) -> { - LocalRepository localRepository = new LocalRepository("main"); - try { - hestiaUtilTestService.setupSolution(files, exercise, localRepository); - } - catch (Exception e) { - fail("Setup solution threw unexpected exception: " + e.getMessage()); - } - return localRepository; - }); - } - - private void test_redirectGetSolutionRepositoryFilesWithoutContent(BiFunction, LocalRepository> setupRepositoryMock) throws Exception { - setupRepositoryMock.apply(programmingExercise, Map.ofEntries(Map.entry("A.java", "abc"), Map.entry("B.java", "cde"), Map.entry("C.java", "efg"))); - - var savedExercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(programmingExercise.getId()); - - // We expect an URL which is the endpoint, with which the file contents can be retrieved - request.getWithForwardedUrl("/api/programming-exercises/" + programmingExercise.getId() + "/file-names", HttpStatus.OK, - "/api/repository/" + savedExercise.getSolutionParticipation().getId() + "/file-names"); - } - void test_redirectGetTemplateRepositoryFilesWithContent() throws Exception { test_redirectGetTemplateRepositoryFilesWithContent((exercise, files) -> { LocalRepository localRepository = new LocalRepository("main"); try { - hestiaUtilTestService.setupTemplate(files, exercise, localRepository); + programmingUtilTestService.setupTemplate(files, exercise, localRepository); } catch (Exception e) { fail("Setup template threw unexpected exception: " + e.getMessage()); @@ -2337,7 +2289,7 @@ void testRedirectGetParticipationRepositoryFilesWithContentAtCommit(String testP var studentLogin = testPrefix + "student1"; try { localRepository.configureRepos("testLocalRepo", "testOriginRepo"); - return hestiaUtilTestService.setupSubmission(files, exercise, localRepository, studentLogin); + return programmingUtilTestService.setupSubmission(files, exercise, localRepository, studentLogin); } catch (Exception e) { fail("Test setup failed"); @@ -2348,8 +2300,13 @@ void testRedirectGetParticipationRepositoryFilesWithContentAtCommit(String testP private void testRedirectGetParticipationRepositoryFilesWithContentAtCommit(BiFunction, ProgrammingSubmission> setupRepositoryMock) throws Exception { - var submission = setupRepositoryMock.apply(programmingExercise, Map.ofEntries(Map.entry("A.java", "abc"), Map.entry("B.java", "cde"), Map.entry("C.java", "efg"))); - String filesWithContentsAsJson = "{\n" + " \"C.java\" : \"efg\",\n" + " \"B.java\" : \"cde\",\n" + " \"A.java\" : \"abc\"\n" + "}"; + var submission = setupRepositoryMock.apply(programmingExercise, Map.of("A.java", "abc", "B.java", "cde", "C.java", "efg")); + String filesWithContentsAsJson = """ + { + "C.java" : "efg", + "B.java" : "cde", + "A.java" : "abc" + }"""; request.getWithFileContents("/api/programming-exercise-participations/" + participation1.getId() + "/files-content/" + submission.getCommitHash(), HttpStatus.OK, filesWithContentsAsJson); @@ -2362,7 +2319,7 @@ void testRedirectGetParticipationRepositoryFilesWithContentAtCommitForbidden(Str var studentLogin = testPrefix + "student1"; try { localRepository.configureRepos("testLocalRepo", "testOriginRepo"); - return hestiaUtilTestService.setupSubmission(files, exercise, localRepository, studentLogin); + return programmingUtilTestService.setupSubmission(files, exercise, localRepository, studentLogin); } catch (Exception e) { fail("Test setup failed"); @@ -2373,7 +2330,7 @@ void testRedirectGetParticipationRepositoryFilesWithContentAtCommitForbidden(Str private void testRedirectGetParticipationRepositoryFilesWithContentAtCommitForbidden( BiFunction, ProgrammingSubmission> setupRepositoryMock) throws Exception { - var submission = setupRepositoryMock.apply(programmingExercise, Map.ofEntries(Map.entry("A.java", "abc"), Map.entry("B.java", "cde"), Map.entry("C.java", "efg"))); + var submission = setupRepositoryMock.apply(programmingExercise, Map.of("A.java", "abc", "B.java", "cde", "C.java", "efg")); request.get("/api/programming-exercise-participations/" + participation1.getId() + "/files-content/" + submission.getCommitHash(), HttpStatus.FORBIDDEN, Map.class); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCIntegrationTest.java index c395a37b3095..1a4022a4a7f6 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCIntegrationTest.java @@ -175,12 +175,6 @@ void testReEvaluateAndUpdateProgrammingExercise_isNotSameGivenExerciseIdInReques programmingExerciseIntegrationTestService.testReEvaluateAndUpdateProgrammingExercise_isNotSameGivenExerciseIdInRequestBody_conflict(); } - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void test_redirectGetSolutionRepositoryFilesWithoutContent() throws Exception { - programmingExerciseIntegrationTestService.test_redirectGetSolutionRepositoryFilesWithoutContent(); - } - @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void test_redirectGetTemplateRepositoryFilesWithContent() throws Exception { diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseResultJenkinsIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseResultJenkinsIntegrationTest.java index 9e0650403412..8340f501af41 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseResultJenkinsIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseResultJenkinsIntegrationTest.java @@ -117,7 +117,7 @@ void shouldRejectCommitsWithoutCommitHash() { CommitDTO dummy = new CommitDTO("", "", ""); var notification = ProgrammingExerciseFactory.generateTestResultDTO(null, Constants.ASSIGNMENT_REPO_NAME, null, ProgrammingLanguage.JAVA, true, List.of("test1", "test2", "test4"), List.of(), new ArrayList<>(), new ArrayList<>(List.of(dummy)), null); - notification.getCommits().clear(); + notification.commits().clear(); programmingExerciseResultTestService.shouldRejectNotificationWithoutCommitHash(notification); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceIntegrationTest.java index e32956ac6bd8..1f0f094f10b2 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceIntegrationTest.java @@ -19,8 +19,6 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.StaticCodeAnalysisCategory; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.LockRepositoryPolicy; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.SubmissionPenaltyPolicy; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.SubmissionPolicy; @@ -45,10 +43,7 @@ void setUp() { programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); // Needed, as we need the test cases for the next steps programmingExercise = programmingExerciseUtilService.loadProgrammingExerciseWithEagerReferences(programmingExercise); - programmingExerciseUtilService.addHintsToExercise(programmingExercise); programmingExerciseUtilService.addTasksToProgrammingExercise(programmingExercise); - programmingExerciseUtilService.addSolutionEntriesToProgrammingExercise(programmingExercise); - programmingExerciseUtilService.addCodeHintsToProgrammingExercise(programmingExercise); programmingExerciseUtilService.addStaticCodeAnalysisCategoriesToProgrammingExercise(programmingExercise); // Load again to fetch changes to statement and hints while keeping eager refs @@ -82,36 +77,13 @@ void importProgrammingExerciseBasis_baseReferencesGotCloned() { final var newTestCaseIDs = newlyImported.getTestCases().stream().map(ProgrammingExerciseTestCase::getId).collect(Collectors.toSet()); assertThat(newlyImported.getTestCases()).hasSameSizeAs(programmingExercise.getTestCases()); assertThat(programmingExercise.getTestCases()).noneMatch(testCase -> newTestCaseIDs.contains(testCase.getId())); - assertThat(programmingExercise.getTestCases()).usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "exercise", "tasks", "solutionEntries", "coverageEntries") + assertThat(programmingExercise.getTestCases()).usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "exercise", "tasks") .containsExactlyInAnyOrderElementsOf(newlyImported.getTestCases()); - final var newHintIDs = newlyImported.getExerciseHints().stream().map(ExerciseHint::getId).collect(Collectors.toSet()); - assertThat(newlyImported.getExerciseHints()).hasSameSizeAs(programmingExercise.getExerciseHints()); - assertThat(programmingExercise.getExerciseHints()).noneMatch(hint -> newHintIDs.contains(hint.getId())); final var newStaticCodeAnalysisCategoriesIDs = newlyImported.getStaticCodeAnalysisCategories().stream().map(StaticCodeAnalysisCategory::getId).collect(Collectors.toSet()); assertThat(newlyImported.getStaticCodeAnalysisCategories()).hasSameSizeAs(programmingExercise.getStaticCodeAnalysisCategories()); assertThat(programmingExercise.getStaticCodeAnalysisCategories()).noneMatch(category -> newStaticCodeAnalysisCategoriesIDs.contains(category.getId())); } - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void importProgrammingExerciseBasis_testsAndHintsHoldTheSameInformation() { - final var imported = importExerciseBase(); - - // All copied hints/tests have the same content are referenced to the new exercise - assertThat(imported.getExerciseHints()).allMatch(hint -> programmingExercise.getExerciseHints().stream().anyMatch( - oldHint -> oldHint.getContent().equals(hint.getContent()) && oldHint.getTitle().equals(hint.getTitle()) && hint.getExercise().getId().equals(imported.getId()))); - assertThat(imported.getExerciseHints().stream().filter(eh -> eh instanceof CodeHint).map(eh -> (CodeHint) eh).collect(Collectors.toSet())) - .allMatch(codeHint -> programmingExercise.getExerciseHints().stream().filter(eh -> eh instanceof CodeHint).map(eh -> (CodeHint) eh) - .anyMatch(oldHint -> oldHint.getTitle().equals(codeHint.getTitle()) - && oldHint.getProgrammingExerciseTask().getTaskName().equals(codeHint.getProgrammingExerciseTask().getTaskName()) - && codeHint.getSolutionEntries().size() == 1 && oldHint.getSolutionEntries().stream().findFirst().orElseThrow().getCode() - .equals(codeHint.getSolutionEntries().stream().findFirst().orElseThrow().getCode()))); - - assertThat(imported.getTestCases()).allMatch(test -> programmingExercise.getTestCases().stream().anyMatch(oldTest -> test.getExercise().getId().equals(imported.getId()) - && oldTest.getTestName().equalsIgnoreCase(test.getTestName()) && oldTest.getWeight().equals(test.getWeight()) && test.getSolutionEntries().size() == 1 - && oldTest.getSolutionEntries().stream().findFirst().orElseThrow().getCode().equals(test.getSolutionEntries().stream().findFirst().orElseThrow().getCode()))); - } - @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") @MethodSource("submissionPolicyProvider") @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTemplateIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTemplateIntegrationTest.java index 2d181a5e7b99..1244f782f744 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTemplateIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTemplateIntegrationTest.java @@ -237,18 +237,6 @@ private Stream languageTypeBuilder() { } argumentBuilder.add(Arguments.of(language, projectType, false)); } - - if (languageFeatures.testwiseCoverageAnalysisSupported()) { - if (projectTypes.isEmpty()) { - argumentBuilder.add(Arguments.of(language, null, true)); - } - for (ProjectType projectType : projectTypes) { - if (projectType == ProjectType.MAVEN_BLACKBOX) { - continue; - } - argumentBuilder.add(Arguments.of(language, projectType, true)); - } - } } return argumentBuilder.build(); } @@ -256,17 +244,17 @@ private Stream languageTypeBuilder() { @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") @MethodSource("languageTypeBuilder") - void testTemplateExercise(ProgrammingLanguage language, ProjectType projectType, boolean testwiseCoverageAnalysis) throws Exception { + void testTemplateExercise(ProgrammingLanguage language, ProjectType projectType) throws Exception { checkPreconditionsForJavaTemplateExecution(projectType); - runTests(language, projectType, exerciseRepo, TestResult.FAILED, testwiseCoverageAnalysis); + runTests(language, projectType, exerciseRepo, TestResult.FAILED); } @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") @MethodSource("languageTypeBuilder") - void testTemplateSolution(ProgrammingLanguage language, ProjectType projectType, boolean testwiseCoverageAnalysis) throws Exception { + void testTemplateSolution(ProgrammingLanguage language, ProjectType projectType) throws Exception { checkPreconditionsForJavaTemplateExecution(projectType); - runTests(language, projectType, solutionRepo, TestResult.SUCCESSFUL, testwiseCoverageAnalysis); + runTests(language, projectType, solutionRepo, TestResult.SUCCESSFUL); } private void checkPreconditionsForJavaTemplateExecution(final ProjectType projectType) { @@ -276,24 +264,20 @@ private void checkPreconditionsForJavaTemplateExecution(final ProjectType projec assumeTrue(java17Home != null, "Could not find Java 17. Skipping execution of template tests."); } - private void runTests(ProgrammingLanguage language, ProjectType projectType, LocalRepository repository, TestResult testResult, boolean testwiseCoverageAnalysis) - throws Exception { + private void runTests(ProgrammingLanguage language, ProjectType projectType, LocalRepository repository, TestResult testResult) throws Exception { exercise.setProgrammingLanguage(language); exercise.setProjectType(projectType); mockConnectorRequestsForSetup(exercise, false, true, false); exercise.setChannelName("exercise-pe"); - if (testwiseCoverageAnalysis) { - exercise.getBuildConfig().setTestwiseCoverageEnabled(true); - } request.postWithResponseBody("/api/programming-exercises/setup", exercise, ProgrammingExercise.class, HttpStatus.CREATED); moveAssignmentSourcesOf(repository); int exitCode; if (projectType != null && projectType.isGradle()) { - exitCode = invokeGradle(testwiseCoverageAnalysis); + exitCode = invokeGradle(); } else { - exitCode = invokeMaven(testwiseCoverageAnalysis); + exitCode = invokeMaven(); } if (TestResult.SUCCESSFUL.equals(testResult)) { @@ -308,14 +292,11 @@ private void runTests(ProgrammingLanguage language, ProjectType projectType, Loc assertThat(testResults).containsExactlyInAnyOrderEntriesOf(Map.of(testResult, 12 + (ProgrammingLanguage.JAVA.equals(language) ? 1 : 0))); } - private int invokeMaven(boolean testwiseCoverageAnalysis) throws MavenInvocationException { + private int invokeMaven() throws MavenInvocationException { InvocationRequest mvnRequest = new DefaultInvocationRequest(); mvnRequest.setJavaHome(java17Home); mvnRequest.setPomFile(testRepo.localRepoFile); mvnRequest.addArgs(List.of("clean", "test")); - if (testwiseCoverageAnalysis) { - mvnRequest.addArg("-Pcoverage"); - } mvnRequest.setShowVersion(true); Invoker mvnInvoker = new DefaultInvoker(); @@ -325,17 +306,11 @@ private int invokeMaven(boolean testwiseCoverageAnalysis) throws MavenInvocation return result.getExitCode(); } - private int invokeGradle(boolean recordTestwiseCoverage) { + private int invokeGradle() { try (ProjectConnection connector = GradleConnector.newConnector().forProjectDirectory(testRepo.localRepoFile).useBuildDistribution().connect()) { BuildLauncher launcher = connector.newBuild(); launcher.setJavaHome(java17Home); - String[] tasks; - if (recordTestwiseCoverage) { - tasks = new String[] { "clean", "test", "tiaTests", "--run-all-tests" }; - } - else { - tasks = new String[] { "clean", "test" }; - } + String[] tasks = new String[] { "clean", "test" }; launcher.forTasks(tasks); launcher.run(); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionAndResultGitlabJenkinsIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionAndResultGitlabJenkinsIntegrationTest.java index fe8821411c84..75fe4787b17b 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionAndResultGitlabJenkinsIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionAndResultGitlabJenkinsIntegrationTest.java @@ -72,7 +72,7 @@ void tearDown() throws Exception { void shouldReceiveBuildLogsOnNewStudentParticipationResult() throws Exception { // Precondition: Database has participation and a programming submission. String userLogin = TEST_PREFIX + "student1"; - var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, false, ProgrammingLanguage.JAVA); + var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, ProgrammingLanguage.JAVA); var exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); exercise = programmingExerciseRepository.findWithEagerStudentParticipationsById(exercise.getId()).orElseThrow(); @@ -102,7 +102,7 @@ void shouldReceiveBuildLogsOnNewStudentParticipationResult() throws Exception { void shouldExtractBuildLogAnalytics_noSca() throws Exception { // Precondition: Database has participation and a programming submission. String userLogin = TEST_PREFIX + "student1"; - Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, false, ProgrammingLanguage.JAVA); + Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, ProgrammingLanguage.JAVA); ProgrammingExercise exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); exercise = programmingExerciseRepository.findWithEagerStudentParticipationsStudentAndLegalSubmissionsById(exercise.getId()).orElseThrow(); var participation = participationUtilService.addStudentParticipationForProgrammingExercise(exercise, userLogin); @@ -138,7 +138,7 @@ private static List getLogs(String dependencyDownloaded, String jobFinis void shouldExtractBuildLogAnalytics_noSca_gradle() throws Exception { // Precondition: Database has participation and a programming submission. String userLogin = TEST_PREFIX + "student1"; - Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, false, ProgrammingLanguage.JAVA); + Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, ProgrammingLanguage.JAVA); ProgrammingExercise exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); exercise = programmingExerciseRepository.findWithEagerStudentParticipationsStudentAndLegalSubmissionsById(exercise.getId()).orElseThrow(); exercise.setProjectType(ProjectType.GRADLE_GRADLE); @@ -168,7 +168,7 @@ void shouldExtractBuildLogAnalytics_noSca_gradle() throws Exception { void shouldExtractBuildLogAnalytics_sca() throws Exception { // Precondition: Database has participation and a programming submission. String userLogin = TEST_PREFIX + "student1"; - Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, false, ProgrammingLanguage.JAVA); + Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, ProgrammingLanguage.JAVA); ProgrammingExercise exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); exercise = programmingExerciseRepository.findWithEagerStudentParticipationsStudentAndLegalSubmissionsById(exercise.getId()).orElseThrow(); var participation = participationUtilService.addStudentParticipationForProgrammingExercise(exercise, userLogin); @@ -196,7 +196,7 @@ void shouldExtractBuildLogAnalytics_sca() throws Exception { void shouldExtractBuildLogAnalytics_unsupportedProgrammingLanguage() throws Exception { // Precondition: Database has participation and a programming submission. String userLogin = TEST_PREFIX + "student1"; - var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, false, ProgrammingLanguage.PYTHON); + var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, ProgrammingLanguage.PYTHON); var exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); var participation = participationUtilService.addStudentParticipationForProgrammingExercise(exercise, userLogin); programmingExerciseUtilService.createProgrammingSubmission(participation, false); @@ -227,7 +227,7 @@ private static Stream shouldSaveBuildLogsOnStudentParticipationArgume void shouldReturnBadRequestWhenPlanKeyDoesntExist(ProgrammingLanguage programmingLanguage, boolean enableStaticCodeAnalysis) throws Exception { // Precondition: Database has participation and a programming submission. String userLogin = TEST_PREFIX + "student1"; - var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(enableStaticCodeAnalysis, false, programmingLanguage); + var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(enableStaticCodeAnalysis, programmingLanguage); exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); exercise = programmingExerciseRepository.findWithEagerStudentParticipationsById(exercise.getId()).orElseThrow(); @@ -297,7 +297,7 @@ private static Stream shouldSaveBuildLogsOnStudentParticipationWithou void shouldSaveBuildLogsOnStudentParticipationWithoutResult(ProgrammingLanguage programmingLanguage, boolean enableStaticCodeAnalysis, boolean buildFailed) throws Exception { // Precondition: Database has participation and a programming submission. String userLogin = TEST_PREFIX + "student1"; - var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(enableStaticCodeAnalysis, false, programmingLanguage); + var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(enableStaticCodeAnalysis, programmingLanguage); exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); exercise = programmingExerciseRepository.findWithEagerStudentParticipationsById(exercise.getId()).orElseThrow(); @@ -324,7 +324,7 @@ void shouldSaveBuildLogsOnStudentParticipationWithoutResult(ProgrammingLanguage void shouldSaveBuildLogsOnStudentParticipationWithoutSubmissionNorResult(ProgrammingLanguage programmingLanguage, boolean enableStaticCodeAnalysis) throws Exception { // Precondition: Database has participation without result and a programming String userLogin = TEST_PREFIX + "student1"; - var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(enableStaticCodeAnalysis, false, programmingLanguage); + var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(enableStaticCodeAnalysis, programmingLanguage); exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); exercise = programmingExerciseRepository.findWithEagerStudentParticipationsById(exercise.getId()).orElseThrow(); @@ -341,7 +341,7 @@ void shouldSaveBuildLogsOnStudentParticipationWithoutSubmissionNorResult(Program @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") void shouldCreateGradleFeedback() throws Exception { String userLogin = TEST_PREFIX + "student1"; - var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, false, JAVA); + var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, JAVA); exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); exercise.setProjectType(ProjectType.GRADLE_GRADLE); exercise = programmingExerciseRepository.save(exercise); @@ -352,7 +352,7 @@ void shouldCreateGradleFeedback() throws Exception { var longErrorMessage = new TestCaseDetailMessageDTO("abc\nmultiline\nfeedback"); var testCase = new TestCaseDTO("test1", "Class", 0d, new ArrayList<>(), List.of(longErrorMessage), new ArrayList<>()); - notification.getResults().getFirst().testCases().set(0, testCase); + notification.results().getFirst().testCases().set(0, testCase); postResult(notification, HttpStatus.OK); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/RepositoryIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/RepositoryIntegrationTest.java index 64307de24282..1fc69fb5eedb 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/RepositoryIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/RepositoryIntegrationTest.java @@ -1201,13 +1201,6 @@ void testLockStudentRepository_beforeStateRepoConfigured() { assertThat(logsList.getFirst().getArgumentArray()).containsExactly(participation.getId()); } - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void testGetSolutionFileNames() throws Exception { - var fileNames = request.get(studentRepoBaseUrl + participation.getId() + "/file-names", HttpStatus.OK, String[].class); - assertThat(fileNames).containsExactly("currentFileName"); - } - private List getFileSubmissions(String fileContent) { List fileSubmissions = new ArrayList<>(); FileSubmission fileSubmission = new FileSubmission(); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/StaticCodeAnalysisIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/StaticCodeAnalysisIntegrationTest.java index 2a6cccd7d15e..0c2220769f0f 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/StaticCodeAnalysisIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/StaticCodeAnalysisIntegrationTest.java @@ -183,7 +183,7 @@ void testResetCategories(ProgrammingLanguage programmingLanguage) throws Excepti doReturn(ObjectId.fromString(dummyHash)).when(gitService).getLastCommitHash(any()); // Create a programming exercise with real categories - var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(true, false, programmingLanguage); + var course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(true, programmingLanguage); ProgrammingExercise exercise = programmingExerciseRepository .findWithTemplateAndSolutionParticipationTeamAssignmentConfigCategoriesById(course.getExercises().iterator().next().getId()).orElseThrow(); staticCodeAnalysisService.createDefaultCategories(exercise); @@ -369,7 +369,7 @@ void testImportCategories_asTutor() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") void testImportCategories_differentLanguages() throws Exception { - ProgrammingExercise sourceExercise = programmingExerciseUtilService.addProgrammingExerciseToCourse(course, true, false, ProgrammingLanguage.SWIFT); + ProgrammingExercise sourceExercise = programmingExerciseUtilService.addProgrammingExerciseToCourse(course, true, ProgrammingLanguage.SWIFT); var endpoint = parameterizeEndpoint("/api/programming-exercises/{exerciseId}/static-code-analysis-categories/import", programmingExerciseSCAEnabled); request.patch(endpoint + "?sourceExerciseId=" + sourceExercise.getId(), null, HttpStatus.CONFLICT); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusBuildScriptGenerationServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusBuildScriptGenerationServiceTest.java index 7bcfb7d3a9aa..73cc266c6a4b 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusBuildScriptGenerationServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusBuildScriptGenerationServiceTest.java @@ -22,11 +22,11 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseBuildConfig; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProjectType; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; +import de.tum.cit.aet.artemis.programming.dto.aeolus.WindfileMetadata; import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusBuildPlanService; import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusBuildScriptGenerationService; import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusTemplateService; -import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile; -import de.tum.cit.aet.artemis.programming.service.aeolus.WindfileMetadata; import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationLocalCILocalVCTest; class AeolusBuildScriptGenerationServiceTest extends AbstractSpringIntegrationLocalCILocalVCTest { @@ -76,10 +76,7 @@ void testBuildScriptGeneration() throws JsonProcessingException { } private Windfile getWindfile() { - Windfile windfile = new Windfile(); - windfile.setApi("v0.0.1"); - windfile.setMetadata(new WindfileMetadata("test", "test", "test", null, null, null, null, null)); - return windfile; + return new Windfile("v0.0.1", new WindfileMetadata("test", "test", "test", null, null, null, null, null), null, null); } private String getSerializedWindfile() throws JsonProcessingException { @@ -97,7 +94,6 @@ void testBuildScriptGenerationUsingBuildPlanGenerationService() throws JsonProce programmingExercise.setProjectType(ProjectType.PLAIN_GRADLE); programmingExercise.setStaticCodeAnalysisEnabled(true); programmingExercise.getBuildConfig().setSequentialTestRuns(true); - programmingExercise.getBuildConfig().setTestwiseCoverageEnabled(true); String script = aeolusBuildScriptGenerationService.getScript(programmingExercise); assertThat(script).isNotNull(); assertThat(script).isEqualTo("imagine a result here"); @@ -105,7 +101,6 @@ void testBuildScriptGenerationUsingBuildPlanGenerationService() throws JsonProce programmingExercise.setProjectType(null); programmingExercise.setStaticCodeAnalysisEnabled(false); programmingExercise.getBuildConfig().setSequentialTestRuns(false); - programmingExercise.getBuildConfig().setTestwiseCoverageEnabled(false); programmingExercise.getBuildConfig().setBuildPlanConfiguration(null); script = aeolusBuildScriptGenerationService.getScript(programmingExercise); assertThat(script).isNotNull(); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusServiceTest.java index 957f33a9cfb8..32520d1db703 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusServiceTest.java @@ -31,13 +31,13 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; +import de.tum.cit.aet.artemis.programming.dto.aeolus.AeolusRepository; +import de.tum.cit.aet.artemis.programming.dto.aeolus.ScriptAction; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; +import de.tum.cit.aet.artemis.programming.dto.aeolus.WindfileMetadata; import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusBuildPlanService; import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusBuildScriptGenerationService; -import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusRepository; import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusTemplateService; -import de.tum.cit.aet.artemis.programming.service.aeolus.ScriptAction; -import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile; -import de.tum.cit.aet.artemis.programming.service.aeolus.WindfileMetadata; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService; import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; @@ -84,9 +84,9 @@ void tearDown() throws Exception { */ @Test void testSuccessfulPublishBuildPlan() throws JsonProcessingException { - Windfile mockWindfile = new Windfile(); var expectedPlanKey = "PLAN"; - mockWindfile.setPreProcessingMetadata("PROJECT-" + expectedPlanKey, null, null, null, null, null, null); + var metadata = new WindfileMetadata(null, "PROJECT-" + expectedPlanKey, null, null, null, null, null, null); + Windfile mockWindfile = new Windfile(null, metadata, null, null); aeolusRequestMockProvider.mockSuccessfulPublishBuildPlan(AeolusTarget.JENKINS, expectedPlanKey); String key = aeolusBuildPlanService.publishBuildPlan(mockWindfile, AeolusTarget.JENKINS); @@ -98,9 +98,9 @@ void testSuccessfulPublishBuildPlan() throws JsonProcessingException { */ @Test void testFailedPublishBuildPlan() throws JsonProcessingException { - Windfile mockWindfile = new Windfile(); var expectedPlanKey = "PLAN"; - mockWindfile.setPreProcessingMetadata("PROJECT-" + expectedPlanKey, null, null, null, null, null, null); + var metadata = new WindfileMetadata(null, "PROJECT-" + expectedPlanKey, null, null, null, null, null, null); + Windfile mockWindfile = new Windfile(null, metadata, null, null); aeolusRequestMockProvider.mockFailedPublishBuildPlan(AeolusTarget.JENKINS); String key = aeolusBuildPlanService.publishBuildPlan(mockWindfile, AeolusTarget.JENKINS); @@ -172,7 +172,7 @@ void testRepositoryMapForHaskellWindfileCreation() throws URISyntaxException { @Test void testReturnsNullonUrlNull() throws JsonProcessingException { ReflectionTestUtils.setField(aeolusBuildPlanService, "ciUrl", null); - assertThat(aeolusBuildPlanService.publishBuildPlan(new Windfile(), AeolusTarget.JENKINS)).isNull(); + assertThat(aeolusBuildPlanService.publishBuildPlan(new Windfile(null, null, null, null), AeolusTarget.JENKINS)).isNull(); } @Test @@ -184,11 +184,8 @@ void testBuildScriptGeneration() throws JsonProcessingException { } private Windfile getWindfile() { - Windfile windfile = new Windfile(); - windfile.setApi("v0.0.1"); - windfile.setMetadata(new WindfileMetadata("test", "test", "test", null, null, null, null, null)); - windfile.setActions(List.of(new ScriptAction())); - return windfile; + var action = new ScriptAction(null, null, null, null, null, false, null, null); + return new Windfile("v0.0.1", new WindfileMetadata("test", "test", "test", null, null, null, null, null), List.of(action), null); } private String getSerializedWindfile() throws JsonProcessingException { @@ -204,17 +201,16 @@ void testShouldNotGenerateAnything() throws JsonProcessingException { programmingExercise.setProjectType(ProjectType.PLAIN_GRADLE); programmingExercise.setStaticCodeAnalysisEnabled(true); programmingExercise.getBuildConfig().setSequentialTestRuns(true); - programmingExercise.getBuildConfig().setTestwiseCoverageEnabled(true); String script = aeolusBuildScriptGenerationService.getScript(programmingExercise); assertThat(script).isNull(); } @Test void testGetWindfileFor() throws IOException { - Windfile windfile = aeolusTemplateService.getWindfileFor(ProgrammingLanguage.JAVA, Optional.empty(), false, false, false); + Windfile windfile = aeolusTemplateService.getWindfileFor(ProgrammingLanguage.JAVA, Optional.empty(), false, false); assertThat(windfile).isNotNull(); - assertThat(windfile.getActions()).isNotNull(); - assertThat(windfile.getActions()).hasSize(1); + assertThat(windfile.actions()).isNotNull(); + assertThat(windfile.actions()).hasSize(1); } @Test @@ -224,7 +220,6 @@ void testGetDefaultWindfileFor() { programmingExercise.setProgrammingLanguage(ProgrammingLanguage.HASKELL); programmingExercise.setStaticCodeAnalysisEnabled(true); programmingExercise.getBuildConfig().setSequentialTestRuns(true); - programmingExercise.getBuildConfig().setTestwiseCoverageEnabled(true); Windfile windfile = aeolusTemplateService.getDefaultWindfileFor(programmingExercise); assertThat(windfile).isNull(); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusTemplateResourceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusTemplateResourceTest.java index 3c82d8858409..ef5c18e18012 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusTemplateResourceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusTemplateResourceTest.java @@ -18,8 +18,8 @@ import de.tum.cit.aet.artemis.core.user.util.UserUtilService; import de.tum.cit.aet.artemis.core.util.RequestUtilService; -import de.tum.cit.aet.artemis.programming.service.aeolus.ScriptAction; -import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile; +import de.tum.cit.aet.artemis.programming.dto.aeolus.ScriptAction; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationLocalCILocalVCTest; class AeolusTemplateResourceTest extends AbstractSpringIntegrationLocalCILocalVCTest { @@ -39,11 +39,10 @@ void initTestCase() { private static Stream templateProvider() { return Stream.of(new Object[][] { { "JAVA/PLAIN_GRADLE", 1 }, { "JAVA/PLAIN_GRADLE?sequentialRuns=true", 2 }, { "JAVA/PLAIN_GRADLE?staticAnalysis=true", 2 }, - { "JAVA/PLAIN_GRADLE?staticAnalysis=true&testCoverage=true", 2 }, { "JAVA/PLAIN_MAVEN", 1 }, { "JAVA/PLAIN_MAVEN?sequentialRuns=true", 2 }, - { "JAVA/PLAIN_MAVEN?staticAnalysis=true", 2 }, { "JAVA/PLAIN_MAVEN?staticAnalysis=true&testCoverage=true", 3 }, { "JAVA/MAVEN_BLACKBOX", 5 }, + { "JAVA/PLAIN_MAVEN", 1 }, { "JAVA/PLAIN_MAVEN?sequentialRuns=true", 2 }, { "JAVA/PLAIN_MAVEN?staticAnalysis=true", 2 }, { "JAVA/MAVEN_BLACKBOX", 5 }, { "JAVA/MAVEN_BLACKBOX?staticAnalysis=true", 6 }, { "ASSEMBLER", 4 }, { "C/FACT", 2 }, { "C/GCC", 3 }, { "C/GCC?staticAnalysis=true", 3 }, { "KOTLIN", 1 }, - { "KOTLIN?testCoverage=true", 2 }, { "KOTLIN?sequentialRuns=true", 3 }, { "VHDL", 4 }, { "HASKELL", 1 }, { "HASKELL?sequentialRuns=true", 2 }, { "OCAML", 2 }, - { "SWIFT/PLAIN", 1 }, { "SWIFT/PLAIN?staticAnalysis=true", 2 } }).map(params -> Arguments.of(params[0], params[1])); + { "KOTLIN?sequentialRuns=true", 3 }, { "VHDL", 4 }, { "HASKELL", 1 }, { "HASKELL?sequentialRuns=true", 2 }, { "OCAML", 2 }, { "SWIFT/PLAIN", 1 }, + { "SWIFT/PLAIN?staticAnalysis=true", 2 } }).map(params -> Arguments.of(params[0], params[1])); } @ParameterizedTest @@ -114,7 +113,7 @@ void testValidWindfileDeserializationWithClass() throws JsonProcessingException Windfile windfile = Windfile.deserialize(validWindfile); assertThat(windfile).isNotNull(); - assertThat(windfile.getActions().getFirst()).isInstanceOf(ScriptAction.class); + assertThat(windfile.actions().getFirst()).isInstanceOf(ScriptAction.class); } @Test @@ -150,13 +149,13 @@ void testValidWindfileWithInvalidAction() { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void testGetNonExistingAeolusTemplateFile() throws Exception { - request.get("/api/aeolus/templates/JAVA/PLAIN_GRADLE?staticAnalysis=true&sequentialRuns=true&testCoverage=true", HttpStatus.NOT_FOUND, String.class); + request.get("/api/aeolus/templates/JAVA/PLAIN_GRADLE?staticAnalysis=true&sequentialRuns=true", HttpStatus.NOT_FOUND, String.class); } void assertWindfileIsCorrect(Windfile windfile, long expectedScriptActions) { - assertThat(windfile.getApi()).isEqualTo("v0.0.1"); - assertThat(windfile.getMetadata().gitCredentials()).isNull(); - assertThat(windfile.getMetadata().docker()).isNotNull(); - assertThat(windfile.getScriptActions().size()).isEqualTo(expectedScriptActions); + assertThat(windfile.api()).isEqualTo("v0.0.1"); + assertThat(windfile.metadata().gitCredentials()).isNull(); + assertThat(windfile.metadata().docker()).isNotNull(); + assertThat(windfile.scriptActions().size()).isEqualTo(expectedScriptActions); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusTest.java index 4010816396cd..816586b6fc60 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/aelous/AeolusTest.java @@ -8,13 +8,13 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusRepository; -import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusResult; -import de.tum.cit.aet.artemis.programming.service.aeolus.DockerConfig; -import de.tum.cit.aet.artemis.programming.service.aeolus.PlatformAction; -import de.tum.cit.aet.artemis.programming.service.aeolus.ScriptAction; -import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile; -import de.tum.cit.aet.artemis.programming.service.aeolus.WindfileMetadata; +import de.tum.cit.aet.artemis.programming.dto.aeolus.AeolusRepository; +import de.tum.cit.aet.artemis.programming.dto.aeolus.AeolusResult; +import de.tum.cit.aet.artemis.programming.dto.aeolus.DockerConfig; +import de.tum.cit.aet.artemis.programming.dto.aeolus.PlatformAction; +import de.tum.cit.aet.artemis.programming.dto.aeolus.ScriptAction; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; +import de.tum.cit.aet.artemis.programming.dto.aeolus.WindfileMetadata; class AeolusTest { @@ -25,45 +25,29 @@ void setup() { DockerConfig dockerConfig = new DockerConfig("image", "tag", List.of("host:container"), List.of("--param1", "--param2")); WindfileMetadata metadata = new WindfileMetadata("name", "id", "description", "author", "gitCredentials", dockerConfig, null, null); - windfile = new Windfile(); - windfile.setApi("v0.0.1"); - windfile.setMetadata(metadata); - - ScriptAction scriptAction = new ScriptAction(); - scriptAction.setName("scriptAction"); - scriptAction.setRunAlways(true); - scriptAction.setScript("script"); - scriptAction.setEnvironment(Map.of("key", "value")); - scriptAction.setParameters(Map.of("key", "value")); AeolusResult scriptResult = new AeolusResult("junit", "text.xml", "ignore", "junit", false); - scriptAction.setResults(List.of(scriptResult)); - assertThat(scriptAction.getResults().getFirst().name()).isEqualTo("junit"); - assertThat(scriptAction.getResults().getFirst().path()).isEqualTo("text.xml"); - assertThat(scriptAction.getResults().getFirst().ignore()).isEqualTo("ignore"); - assertThat(scriptAction.getResults().getFirst().type()).isEqualTo("junit"); - assertThat(scriptAction.getResults().getFirst().before()).isEqualTo(false); - - PlatformAction platformAction = new PlatformAction(); - platformAction.setName("platformAction"); - platformAction.setWorkdir("workdir"); - platformAction.setRunAlways(true); - platformAction.setPlatform("jenkins"); - platformAction.setPlatform("jenkins"); - platformAction.setKind("junit"); + ScriptAction scriptAction = new ScriptAction("scriptAction", Map.of("key", "value"), Map.of("key", "value"), List.of(scriptResult), null, true, null, "script"); + assertThat(scriptAction.results().getFirst().name()).isEqualTo("junit"); + assertThat(scriptAction.results().getFirst().path()).isEqualTo("text.xml"); + assertThat(scriptAction.results().getFirst().ignore()).isEqualTo("ignore"); + assertThat(scriptAction.results().getFirst().type()).isEqualTo("junit"); + assertThat(scriptAction.results().getFirst().before()).isEqualTo(false); + AeolusResult result = new AeolusResult("name", "path", "ignore", "type", true); - platformAction.setResults(List.of(result)); - assertThat(platformAction.getResults().getFirst().name()).isEqualTo("name"); - assertThat(platformAction.getResults().getFirst().path()).isEqualTo("path"); - assertThat(platformAction.getResults().getFirst().ignore()).isEqualTo("ignore"); - assertThat(platformAction.getResults().getFirst().type()).isEqualTo("type"); - assertThat(platformAction.getResults().getFirst().before()).isEqualTo(true); - - windfile.setActions(List.of(scriptAction, platformAction)); + PlatformAction platformAction = new PlatformAction("platformAction", Map.of("key", "value"), Map.of("key", "value"), List.of(result), "workdir", true, "jenkins", "junit", + "type"); + assertThat(platformAction.results().getFirst().name()).isEqualTo("name"); + assertThat(platformAction.results().getFirst().path()).isEqualTo("path"); + assertThat(platformAction.results().getFirst().ignore()).isEqualTo("ignore"); + assertThat(platformAction.results().getFirst().type()).isEqualTo("type"); + assertThat(platformAction.results().getFirst().before()).isEqualTo(true); + + windfile = new Windfile("v0.0.1", metadata, List.of(scriptAction, platformAction), null); } @Test void testGetResults() { - var results = windfile.getResults(); + var results = windfile.results(); assertThat(results.size()).isEqualTo(2); assertThat(results.getFirst().name()).isEqualTo("junit"); assertThat(results.getFirst().path()).isEqualTo("text.xml"); @@ -79,43 +63,25 @@ void testGetResults() { @Test void testWindfileGetterAndSetter() { - assertThat(windfile.getApi()).isEqualTo("v0.0.1"); - assertThat(windfile.getMetadata().author()).isEqualTo("author"); - assertThat(windfile.getMetadata().id()).isEqualTo("id"); - assertThat(windfile.getMetadata().description()).isEqualTo("description"); + assertThat(windfile.api()).isEqualTo("v0.0.1"); + assertThat(windfile.metadata().author()).isEqualTo("author"); + assertThat(windfile.metadata().id()).isEqualTo("id"); + assertThat(windfile.metadata().description()).isEqualTo("description"); DockerConfig dockerConfig = new DockerConfig("image", "tag", List.of("host:container"), List.of("--param1", "--param2")); - assertThat(windfile.getMetadata().docker()).isEqualTo(dockerConfig); - assertThat(windfile.getMetadata().name()).isEqualTo("name"); - assertThat(windfile.getMetadata().gitCredentials()).isEqualTo("gitCredentials"); - assertThat(windfile.getActions().getFirst().getName()).isEqualTo("scriptAction"); - assertThat(windfile.getActions().getFirst().isRunAlways()).isEqualTo(true); - ScriptAction scriptAction = (ScriptAction) windfile.getActions().getFirst(); - assertThat(scriptAction.getScript()).isEqualTo("script"); - assertThat(scriptAction.getEnvironment()).isEqualTo(Map.of("key", "value")); - assertThat(scriptAction.getParameters()).isEqualTo(Map.of("key", "value")); - PlatformAction platformAction = (PlatformAction) windfile.getActions().get(1); - platformAction.setKind("junit"); - assertThat(platformAction.getKind()).isEqualTo("junit"); - platformAction.setType("type"); - assertThat(platformAction.getType()).isEqualTo("type"); - assertThat(platformAction.getWorkdir()).isEqualTo("workdir"); - assertThat(platformAction.getName()).isEqualTo("platformAction"); - assertThat(platformAction.isRunAlways()).isEqualTo(true); - assertThat(platformAction.getPlatform()).isEqualTo("jenkins"); - } - - @Test - void testSettersWithoutMetadata() { - windfile.setMetadata(null); - AeolusRepository aeolusRepository = new AeolusRepository("url", "branch", "path"); - windfile.setPreProcessingMetadata("id", "name", "gitCredentials", "resultHook", "description", Map.of("key", aeolusRepository), "resultHookCredentials"); - assertThat(windfile.getMetadata().id()).isEqualTo("id"); - assertThat(windfile.getMetadata().description()).isEqualTo("description"); - assertThat(windfile.getMetadata().name()).isEqualTo("name"); - assertThat(windfile.getRepositories().get("key")).isEqualTo(aeolusRepository); - assertThat(windfile.getMetadata().gitCredentials()).isEqualTo("gitCredentials"); - assertThat(windfile.getMetadata().resultHook()).isEqualTo("resultHook"); - assertThat(windfile.getMetadata().resultHookCredentials()).isEqualTo("resultHookCredentials"); + assertThat(windfile.metadata().docker()).isEqualTo(dockerConfig); + assertThat(windfile.metadata().name()).isEqualTo("name"); + assertThat(windfile.metadata().gitCredentials()).isEqualTo("gitCredentials"); + assertThat(windfile.actions().getFirst().name()).isEqualTo("scriptAction"); + assertThat(windfile.actions().getFirst().runAlways()).isEqualTo(true); + ScriptAction scriptAction = (ScriptAction) windfile.actions().getFirst(); + assertThat(scriptAction.script()).isEqualTo("script"); + assertThat(scriptAction.environment()).isEqualTo(Map.of("key", "value")); + assertThat(scriptAction.parameters()).isEqualTo(Map.of("key", "value")); + PlatformAction platformAction = (PlatformAction) windfile.actions().get(1); + assertThat(platformAction.workdir()).isEqualTo("workdir"); + assertThat(platformAction.name()).isEqualTo("platformAction"); + assertThat(platformAction.runAlways()).isEqualTo(true); + assertThat(platformAction.platform()).isEqualTo("jenkins"); } @Test diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintIntegrationTest.java deleted file mode 100644 index a49fe18c881c..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintIntegrationTest.java +++ /dev/null @@ -1,147 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import org.springframework.security.test.context.support.WithMockUser; - -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; - -class CodeHintIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { - - private static final String TEST_PREFIX = "codehint"; - - private ProgrammingExercise exercise; - - private CodeHint codeHint; - - private ProgrammingExerciseSolutionEntry solutionEntry; - - @BeforeEach - void initTestCase() { - userUtilService.addUsers(TEST_PREFIX, 1, 1, 1, 1); - - final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExerciseAndTestCases(); - exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void generateCodeHintsForAnExerciseAsAStudent() throws Exception { - request.postListWithResponseBody("/api/programming-exercises/" + exercise.getId() + "/code-hints", null, CodeHint.class, HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void generateCodeHintsForAnExerciseAsATutor() throws Exception { - request.postListWithResponseBody("/api/programming-exercises/" + exercise.getId() + "/code-hints", null, CodeHint.class, HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void generateCodeHintsForAnExerciseAsAnEditor() throws Exception { - request.postListWithResponseBody("/api/programming-exercises/" + exercise.getId() + "/code-hints", null, CodeHint.class, HttpStatus.OK); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void generateCodeHintsForAnExerciseAsAnInstructor() throws Exception { - request.postListWithResponseBody("/api/programming-exercises/" + exercise.getId() + "/code-hints", null, CodeHint.class, HttpStatus.OK); - } - - private void addCodeHints() { - exercise = programmingExerciseUtilService.loadProgrammingExerciseWithEagerReferences(exercise); - programmingExerciseUtilService.addHintsToExercise(exercise); - programmingExerciseUtilService.addTasksToProgrammingExercise(exercise); - programmingExerciseUtilService.addSolutionEntriesToProgrammingExercise(exercise); - programmingExerciseUtilService.addCodeHintsToProgrammingExercise(exercise); - Set hints = codeHintRepository.findByExerciseId(exercise.getId()); - codeHint = hints.stream().filter(hint -> "Task for test1".equals(hint.getProgrammingExerciseTask().getTaskName())).findFirst().orElseThrow(); - codeHint = codeHintRepository.findByIdWithSolutionEntriesElseThrow(codeHint.getId()); - solutionEntry = codeHint.getSolutionEntries().stream().findFirst().orElseThrow(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void removeSolutionEntryFromCodeHintAsAStudent() throws Exception { - addCodeHints(); - request.delete("/api/programming-exercises/" + exercise.getId() + "/code-hints/" + codeHint.getId() + "/solution-entries/" + solutionEntry.getId(), HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void removeSolutionEntryFromCodeHintAsATutor() throws Exception { - addCodeHints(); - request.delete("/api/programming-exercises/" + exercise.getId() + "/code-hints/" + codeHint.getId() + "/solution-entries/" + solutionEntry.getId(), HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void removeSolutionEntryFromCodeHintAsAnEditor() throws Exception { - addCodeHints(); - request.delete("/api/programming-exercises/" + exercise.getId() + "/code-hints/" + codeHint.getId() + "/solution-entries/" + solutionEntry.getId(), HttpStatus.NO_CONTENT); - assertThat(codeHintRepository.findByIdWithSolutionEntriesElseThrow(codeHint.getId()).getSolutionEntries()).isEmpty(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void removeSolutionEntryFromCodeHintAsAnInstructor() throws Exception { - addCodeHints(); - request.delete("/api/programming-exercises/" + exercise.getId() + "/code-hints/" + codeHint.getId() + "/solution-entries/" + solutionEntry.getId(), HttpStatus.NO_CONTENT); - assertThat(codeHintRepository.findByIdWithSolutionEntriesElseThrow(codeHint.getId()).getSolutionEntries()).isEmpty(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void updateSolutionEntriesOnSaving() throws Exception { - addCodeHints(); - var solutionEntries = codeHint.getSolutionEntries().stream().toList(); - - Map testCases = testCaseRepository.findByExerciseId(exercise.getId()).stream() - .collect(Collectors.toMap(ProgrammingExerciseTestCase::getTestName, test -> test)); - - var changedEntry = solutionEntries.getFirst(); - changedEntry.setLine(100); - changedEntry.setPreviousLine(200); - changedEntry.setCode("Changed code"); - changedEntry.setPreviousCode("Changed previous code"); - changedEntry.setTestCase(testCases.get("test3")); - - var newEntry = new ProgrammingExerciseSolutionEntry(); - newEntry.setLine(200); - newEntry.setPreviousLine(300); - newEntry.setCode("New code"); - newEntry.setPreviousCode("New previous code"); - var testCase = testCases.get("test1"); - newEntry.setTestCase(testCase); - var savedNewEntry = programmingExerciseSolutionEntryRepository.save(newEntry); - savedNewEntry.setTestCase(testCase); - codeHint.setSolutionEntries(new HashSet<>(Set.of(changedEntry, savedNewEntry))); - - request.put("/api/programming-exercises/" + exercise.getId() + "/exercise-hints/" + codeHint.getId(), codeHint, HttpStatus.OK); - - var updatedHint = codeHintRepository.findByIdWithSolutionEntriesElseThrow(codeHint.getId()); - assertThat(updatedHint.getSolutionEntries()).containsExactlyInAnyOrder(changedEntry, savedNewEntry); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGetAllCodeHints() throws Exception { - addCodeHints(); - - var actualCodeHints = request.getList("/api/programming-exercises/" + exercise.getId() + "/code-hints", HttpStatus.OK, CodeHint.class); - assertThat(actualCodeHints).hasSize(3); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintServiceTest.java deleted file mode 100644 index 05a72ac9a78d..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintServiceTest.java +++ /dev/null @@ -1,249 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.security.test.context.support.WithMockUser; - -import de.tum.cit.aet.artemis.assessment.domain.Visibility; -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTestCaseType; - -@SuppressWarnings("ArraysAsListWithZeroOrOneArgument") -class CodeHintServiceTest extends AbstractProgrammingIntegrationIndependentTest { - - private static final String TEST_PREFIX = "codehintservice"; - - private ProgrammingExercise exercise; - - @BeforeEach - void initTestCase() { - userUtilService.addUsers(TEST_PREFIX, 0, 0, 0, 1); - final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(); - exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - } - - private ProgrammingExerciseTestCase addTestCaseToExercise(String name) { - var testCase = new ProgrammingExerciseTestCase(); - testCase.setTestName(name); - testCase.setExercise(exercise); - testCase.setVisibility(Visibility.ALWAYS); - testCase.setActive(true); - testCase.setWeight(1D); - testCase.setType(ProgrammingExerciseTestCaseType.BEHAVIORAL); - return testCaseRepository.save(testCase); - } - - private ProgrammingExerciseSolutionEntry addSolutionEntryToTestCase(ProgrammingExerciseTestCase testCase) { - var solutionEntry = new ProgrammingExerciseSolutionEntry(); - solutionEntry.setTestCase(testCase); - solutionEntry.setLine(1); - solutionEntry.setCode("code"); - return programmingExerciseSolutionEntryRepository.save(solutionEntry); - } - - private ProgrammingExerciseTask addTaskToExercise(String name, List testCases) { - var task = new ProgrammingExerciseTask(); - task.setExercise(exercise); - task.setTaskName(name); - task = taskRepository.save(task); - for (int i = 0; i < testCases.size(); i++) { - ProgrammingExerciseTestCase testCase = testCases.get(i); - testCase.getTasks().add(task); - testCases.set(i, testCaseRepository.save(testCase)); - } - task.setTestCases(new HashSet<>(testCases)); - return task; - } - - private CodeHint addCodeHintToTask(String name, ProgrammingExerciseTask task, Set solutionEntries) { - var codeHint = new CodeHint(); - codeHint.setTitle(name); - codeHint.setProgrammingExerciseTask(task); - codeHint.setExercise(exercise); - codeHint.setSolutionEntries(solutionEntries); - - solutionEntries.forEach(entry -> entry.setCodeHint(codeHint)); - var createdHint = codeHintRepository.save(codeHint); - programmingExerciseSolutionEntryRepository.saveAll(solutionEntries); - return createdHint; - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationWithNoSolutionEntry() { - var testCase = addTestCaseToExercise("TestCase1"); - addTaskToExercise("Task1", Arrays.asList(testCase)); - - var codeHints = codeHintService.generateCodeHintsForExercise(exercise, true); - assertThat(codeHints).isEmpty(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationWithOneSolutionEntry() { - var testCase = addTestCaseToExercise("TestCase1"); - var solutionEntry = addSolutionEntryToTestCase(testCase); - var task = addTaskToExercise("Task1", Arrays.asList(testCase)); - - var codeHints = codeHintService.generateCodeHintsForExercise(exercise, true); - assertThat(codeHints).hasSize(1); - assertThat(codeHints.getFirst().getProgrammingExerciseTask()).isEqualTo(task); - assertThat(codeHints.getFirst().getSolutionEntries()).containsExactly(solutionEntry); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationTwiceShouldDeleteOldHint() { - var testCase = addTestCaseToExercise("TestCase1"); - var solutionEntry = addSolutionEntryToTestCase(testCase); - var task = addTaskToExercise("Task1", Arrays.asList(testCase)); - - var codeHints = codeHintService.generateCodeHintsForExercise(exercise, true); - assertThat(codeHints).hasSize(1); - var codeHint = codeHints.getFirst(); - - codeHints = codeHintService.generateCodeHintsForExercise(exercise, true); - assertThat(codeHints).hasSize(1); - assertThat(codeHints.getFirst()).isNotEqualTo(codeHint); - assertThat(codeHints.getFirst().getProgrammingExerciseTask()).isEqualTo(task); - assertThat(codeHints.getFirst().getSolutionEntries()).containsExactly(solutionEntry); - - final Set codeHintsAfterSaving = codeHintRepository.findByExerciseId(exercise.getId()); - assertThat(codeHintsAfterSaving).hasSize(1); - assertThat(codeHintsAfterSaving.stream().findAny().orElseThrow()).isNotEqualTo(codeHint).isEqualTo(codeHints.getFirst()); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationTwiceShouldNotDeleteOldHint() { - var testCase = addTestCaseToExercise("TestCase1"); - var solutionEntry = addSolutionEntryToTestCase(testCase); - var task = addTaskToExercise("Task1", Arrays.asList(testCase)); - - var codeHints = codeHintService.generateCodeHintsForExercise(exercise, false); - assertThat(codeHints).hasSize(1); - var codeHint = codeHints.getFirst(); - - codeHints = codeHintService.generateCodeHintsForExercise(exercise, false); - assertThat(codeHints).hasSize(1); - assertThat(codeHints.getFirst()).isNotEqualTo(codeHint); - assertThat(codeHints.getFirst().getProgrammingExerciseTask()).isEqualTo(task); - assertThat(codeHints.getFirst().getSolutionEntries()).containsExactly(solutionEntry); - assertThat(codeHintRepository.findByExerciseId(exercise.getId())).containsExactlyInAnyOrder(codeHint, codeHints.getFirst()); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testUpdateTestCaseOfSolutionEntry() { - var testCase1 = addTestCaseToExercise("testCase1"); - var testCase2 = addTestCaseToExercise("testCase2"); - var entry = addSolutionEntryToTestCase(testCase1); - var task = addTaskToExercise("task", new ArrayList<>(List.of(testCase1, testCase2))); - var codeHint = addCodeHintToTask("codeHint1", task, new HashSet<>(Set.of(entry))); - - var entryToUpdate = codeHint.getSolutionEntries().stream().findFirst().orElseThrow(); - entryToUpdate.setTestCase(testCase2); - codeHintService.updateSolutionEntriesForCodeHint(codeHint); - - var allEntries = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); - assertThat(allEntries).hasSize(1); - assertThat(allEntries.stream().findAny().orElseThrow().getTestCase().getId()).isEqualTo(testCase2.getId()); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testUpdatedContentOfSolutionEntry() { - var testCase1 = addTestCaseToExercise("testCase"); - var entry = addSolutionEntryToTestCase(testCase1); - var task = addTaskToExercise("task", new ArrayList<>(List.of(testCase1))); - var codeHint = addCodeHintToTask("codeHint", task, new HashSet<>(Set.of(entry))); - - var entryToUpdate = codeHint.getSolutionEntries().stream().findFirst().orElseThrow(); - entryToUpdate.setLine(120); - entry.setPreviousLine(130); - entryToUpdate.setCode("Updated code"); - entry.setPreviousCode("Updated previous code"); - entry.setFilePath("Updated file path"); - codeHintService.updateSolutionEntriesForCodeHint(codeHint); - - var allEntries = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); - assertThat(allEntries).hasSize(1); - assertThat(allEntries.stream().findAny().orElseThrow()).isEqualTo(entryToUpdate); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testSaveWithNewSolutionEntry() { - // the entry has been created and persisted, but not assigned to the hint yet - var testCase = addTestCaseToExercise("testCase"); - var manuallyCreatedEntry = addSolutionEntryToTestCase(testCase); - var task = addTaskToExercise("task", new ArrayList<>(List.of(testCase))); - var codeHint = addCodeHintToTask("codeHint", task, new HashSet<>(Collections.emptySet())); - - codeHint.setSolutionEntries(new HashSet<>(Set.of(manuallyCreatedEntry))); - codeHintService.updateSolutionEntriesForCodeHint(codeHint); - - var allEntries = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); - assertThat(allEntries).containsExactly(manuallyCreatedEntry); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testSaveWithRemovedSolutionEntry() { - // the entry has been created and persisted, but not assigned to the hint yet - var testCase = addTestCaseToExercise("testCase"); - var entryToRemove = addSolutionEntryToTestCase(testCase); - var task = addTaskToExercise("task", new ArrayList<>(List.of(testCase))); - var codeHint = addCodeHintToTask("codeHint", task, new HashSet<>(Set.of(entryToRemove))); - - codeHint.setSolutionEntries(new HashSet<>(Collections.emptySet())); - codeHintService.updateSolutionEntriesForCodeHint(codeHint); - - var entriesForHint = programmingExerciseSolutionEntryRepository.findByCodeHintId(codeHint.getId()); - assertThat(entriesForHint).isEmpty(); - - var allEntries = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); - assertThat(allEntries).containsExactly(entryToRemove); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void testSaveEntryWithTestCaseUnrelatedToHintTask() { - // the test case of an entry belongs to a task unequal to the task of the hint that is updated - var unrelatedTestCase = addTestCaseToExercise("unrelatedTaskTestCase"); - addTaskToExercise("unrelatedTask", new ArrayList<>(List.of(unrelatedTestCase))); - var invalidSolutionEntry = new ProgrammingExerciseSolutionEntry(); - invalidSolutionEntry.setTestCase(unrelatedTestCase); - invalidSolutionEntry.setCode("abc"); - - var relatedTestCase = addTestCaseToExercise("relatedTaskTestCase"); - var relatedTask = addTaskToExercise("relatedTask", new ArrayList<>(List.of(relatedTestCase))); - var codeHint = addCodeHintToTask("codeHint", relatedTask, new HashSet<>(Collections.emptySet())); - - codeHint.setSolutionEntries(new HashSet<>(Set.of(invalidSolutionEntry))); - assertThatExceptionOfType(BadRequestAlertException.class).isThrownBy(() -> codeHintService.updateSolutionEntriesForCodeHint(codeHint)); - - var entriesForHint = programmingExerciseSolutionEntryRepository.findByCodeHintId(codeHint.getId()); - assertThat(entriesForHint).isEmpty(); - - var allEntries = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); - assertThat(allEntries).isEmpty(); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintIntegrationTest.java deleted file mode 100644 index 6a44f54820fe..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintIntegrationTest.java +++ /dev/null @@ -1,457 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import org.springframework.security.test.context.support.WithMockUser; - -import de.tum.cit.aet.artemis.assessment.domain.AssessmentType; -import de.tum.cit.aet.artemis.assessment.domain.Feedback; -import de.tum.cit.aet.artemis.assessment.domain.FeedbackType; -import de.tum.cit.aet.artemis.assessment.domain.Result; -import de.tum.cit.aet.artemis.assessment.domain.Visibility; -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHintActivation; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; - -class ExerciseHintIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { - - private static final String TEST_PREFIX = "exercisehintintegration"; - - private ProgrammingExercise exercise; - - private ProgrammingExercise exerciseLite; - - private List hints; - - private ExerciseHint exerciseHint; - - private ProgrammingExerciseStudentParticipation studentParticipation; - - private int timeOffset = 0; - - @BeforeEach - void initTestCase() { - final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExerciseAndTestCases(); - final ProgrammingExercise programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - - userUtilService.addUsers(TEST_PREFIX, 2, 2, 1, 2); - - testCaseRepository.saveAll(testCaseRepository.findByExerciseId(programmingExercise.getId()).stream().peek(testCase -> testCase.setActive(true)).toList()); - exerciseLite = programmingExerciseRepository.findByIdElseThrow(programmingExercise.getId()); - exercise = programmingExerciseUtilService.loadProgrammingExerciseWithEagerReferences(exerciseLite); - programmingExerciseUtilService.addHintsToExercise(exercise); - programmingExerciseUtilService.addTasksToProgrammingExercise(exercise); - - List sortedTasks = programmingExerciseTaskService.getSortedTasks(exercise); - - hints = new ArrayList<>(exerciseHintRepository.findByExerciseId(exerciseLite.getId())); - exerciseHint = hints.getFirst(); - exerciseHint.setProgrammingExerciseTask(sortedTasks.getFirst()); - hints.get(1).setProgrammingExerciseTask(sortedTasks.get(1)); - hints.get(2).setProgrammingExerciseTask(sortedTasks.get(2)); - exerciseHintRepository.saveAll(hints); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void queryAllAvailableHintsForAnExercise() throws Exception { - var user = userTestRepository.getUserWithGroupsAndAuthorities(); - studentParticipation = participationUtilService.addStudentParticipationForProgrammingExercise(exercise, user.getLogin()); - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - - var availableHints = request.getList("/api/programming-exercises/" + exercise.getId() + "/exercise-hints/available", HttpStatus.OK, ExerciseHint.class); - assertThat(availableHints).hasSize(1); - assertThat(availableHints.getFirst().getContent()).isNullOrEmpty(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void queryAllActivatedHintsForAnExercise() throws Exception { - var user = userTestRepository.getUserWithGroupsAndAuthorities(); - var ueha = new ExerciseHintActivation(); - ueha.setExerciseHint(exerciseHint); - ueha.setUser(user); - ueha.setActivationDate(ZonedDateTime.now()); - ueha.setRating(4); - exerciseHintActivationRepository.save(ueha); - - var availableHints = request.getList("/api/programming-exercises/" + exercise.getId() + "/exercise-hints/activated", HttpStatus.OK, ExerciseHint.class); - assertThat(availableHints).hasSize(1); - assertThat(availableHints.getFirst().getId()).isEqualTo(exerciseHint.getId()); - assertThat(availableHints.getFirst().getContent()).isEqualTo(exerciseHint.getContent()); - assertThat(availableHints.getFirst().getCurrentUserRating()).isEqualTo(4); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void activateHintForAnExercise() throws Exception { - var user = userTestRepository.getUserWithGroupsAndAuthorities(); - studentParticipation = participationUtilService.addStudentParticipationForProgrammingExercise(exercise, user.getLogin()); - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - - var activatedHint = request.postWithResponseBody("/api/programming-exercises/" + exercise.getId() + "/exercise-hints/" + exerciseHint.getId() + "/activate", null, - ExerciseHint.class, HttpStatus.OK); - assertThat(activatedHint.getId()).isEqualTo(exerciseHint.getId()); - assertThat(activatedHint.getContent()).isEqualTo(exerciseHint.getContent()); - - var uehas = exerciseHintActivationRepository.findByExerciseAndUserWithExerciseHintRelations(exercise.getId(), user.getId()); - assertThat(uehas).hasSize(1); - assertThat(uehas.stream().findFirst().get().getExerciseHint().getId()).isEqualTo(exerciseHint.getId()); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void rateActivatedHintForAnExercise() throws Exception { - var user = userTestRepository.getUserWithGroupsAndAuthorities(); - var ueha = new ExerciseHintActivation(); - ueha.setExerciseHint(exerciseHint); - ueha.setUser(user); - ueha.setActivationDate(ZonedDateTime.now()); - exerciseHintActivationRepository.save(ueha); - - request.postWithoutLocation("/api/programming-exercises/" + exercise.getId() + "/exercise-hints/" + exerciseHint.getId() + "/rating/" + 4, null, HttpStatus.OK, null); - - ueha = exerciseHintActivationRepository.findById(ueha.getId()).orElseThrow(); - assertThat(ueha.getRating()).isEqualTo(4); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void rateActivatedHintForAnExerciseBadRequest() throws Exception { - var user = userTestRepository.getUserWithGroupsAndAuthorities(); - var ueha = new ExerciseHintActivation(); - ueha.setExerciseHint(exerciseHint); - ueha.setUser(user); - ueha.setActivationDate(ZonedDateTime.now()); - exerciseHintActivationRepository.save(ueha); - - request.postWithoutLocation("/api/programming-exercises/" + exercise.getId() + "/exercise-hints/" + exerciseHint.getId() + "/rating/" + 100, null, HttpStatus.BAD_REQUEST, - null); - - ueha = exerciseHintActivationRepository.findById(ueha.getId()).orElseThrow(); - assertThat(ueha.getRating()).isNull(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void rateNotActivatedHintForAnExerciseForbidden() throws Exception { - long sizeBefore = exerciseHintActivationRepository.count(); - request.postWithoutLocation("/api/programming-exercises/" + exercise.getId() + "/exercise-hints/" + exerciseHint.getId() + "/rating/" + 4, null, HttpStatus.NOT_FOUND, - null); - assertThat(exerciseHintActivationRepository.count()).isEqualTo(sizeBefore); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void queryAllHintsForAnExerciseAsAStudent() throws Exception { - request.getList("/api/programming-exercises/" + exercise.getId() + "/exercise-hints", HttpStatus.FORBIDDEN, ExerciseHint.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void queryAllHintsForAnExerciseAsATutor() throws Exception { - request.getList("/api/programming-exercises/" + exercise.getId() + "/exercise-hints", HttpStatus.OK, ExerciseHint.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void queryAllHintsForAnExerciseAsAnInstructor() throws Exception { - request.getList("/api/programming-exercises/" + exercise.getId() + "/exercise-hints", HttpStatus.OK, ExerciseHint.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void getHintForAnExerciseAsStudentShouldReturnForbidden() throws Exception { - request.get("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints/" + exerciseHint.getId(), HttpStatus.FORBIDDEN, ExerciseHint.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void getHintForAnExerciseAsTutor() throws Exception { - request.get("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints/" + exerciseHint.getId(), HttpStatus.OK, ExerciseHint.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void getHintForAnExerciseAsEditor() throws Exception { - request.get("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints/" + exerciseHint.getId(), HttpStatus.OK, ExerciseHint.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void getHintForAnExerciseAsAnInstructor() throws Exception { - request.get("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints/" + exerciseHint.getId(), HttpStatus.OK, ExerciseHint.class); - request.get("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints/" + 0L, HttpStatus.NOT_FOUND, ExerciseHint.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void createHintAsAStudentShouldReturnForbidden() throws Exception { - ExerciseHint exerciseHint = new ExerciseHint().content("content 4").title("title 4").exercise(exerciseLite); - request.post("/api/programming-exercises/" + exercise.getId() + "/exercise-hints", exerciseHint, HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void createHintAsTutorForbidden() throws Exception { - ExerciseHint exerciseHint = new ExerciseHint().content("content 4").title("title 4").exercise(exerciseLite); - request.post("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints", exerciseHint, HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void createHintAsEditor() throws Exception { - ExerciseHint exerciseHint = new ExerciseHint().content("content 4").title("title 4").exercise(exerciseLite); - request.post("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints", exerciseHint, HttpStatus.CREATED); - - Set exerciseHints = exerciseHintRepository.findByExerciseId(exerciseLite.getId()); - assertThat(exerciseHints).hasSize(4); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void createHintAsInstructor() throws Exception { - ExerciseHint exerciseHint = new ExerciseHint().content("content 4").title("title 4").exercise(exerciseLite); - - request.post("/api/programming-exercises/" + exercise.getId() + "/exercise-hints", exerciseHint, HttpStatus.CREATED); - Set exerciseHints = exerciseHintRepository.findByExerciseId(exerciseLite.getId()); - assertThat(exerciseHints).hasSize(4); - - exerciseHint.setExercise(null); - request.post("/api/programming-exercises/" + exercise.getId() + "/exercise-hints", exerciseHint, HttpStatus.CONFLICT); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void createCodeHintShouldFail() throws Exception { - CodeHint codeHint = new CodeHint(); - codeHint.setTitle("Hint 1"); - codeHint.setExercise(exercise); - - long sizeBefore = exerciseHintRepository.count(); - request.post("/api/programming-exercises/" + codeHint.getExercise().getId() + "/exercise-hints", codeHint, HttpStatus.BAD_REQUEST); - long sizeAfter = exerciseHintRepository.count(); - assertThat(sizeAfter).isEqualTo(sizeBefore); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void updateHintAsAStudentShouldReturnForbidden() throws Exception { - updateHintForbidden(); - } - - private void updateHintForbidden() throws Exception { - String newContent = "new content value!"; - String contentBefore = exerciseHint.getContent(); - exerciseHint.setContent(newContent); - request.put("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints/" + exerciseHint.getId(), exerciseHint, HttpStatus.FORBIDDEN); - - Optional hintAfterSave = exerciseHintRepository.findById(exerciseHint.getId()); - assertThat(hintAfterSave).isPresent(); - assertThat(hintAfterSave.get()).isInstanceOf(ExerciseHint.class); - assertThat((hintAfterSave.get()).getContent()).isEqualTo(contentBefore); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void updateHintAsTutorForbidden() throws Exception { - updateHintForbidden(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void updateHintAsEditor() throws Exception { - String newContent = "new content value!"; - exerciseHint.setContent(newContent); - request.put("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints/" + exerciseHint.getId(), exerciseHint, HttpStatus.OK); - Optional hintAfterSave = exerciseHintRepository.findById(exerciseHint.getId()); - assertThat(hintAfterSave).isPresent(); - assertThat(hintAfterSave.get()).isInstanceOf(ExerciseHint.class); - assertThat((hintAfterSave.get()).getContent()).isEqualTo(newContent); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void updateHintAsInstructor() throws Exception { - String newContent = "new content value!"; - - exerciseHint.setContent(newContent); - request.put("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints/" + exerciseHint.getId(), exerciseHint, HttpStatus.OK); - - Optional hintAfterSave = exerciseHintRepository.findById(exerciseHint.getId()); - assertThat(hintAfterSave).isPresent(); - assertThat(hintAfterSave.get()).isInstanceOf(ExerciseHint.class); - assertThat((hintAfterSave.get()).getContent()).isEqualTo(newContent); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void updateCodeHintTitle() throws Exception { - CodeHint codeHint = new CodeHint(); - codeHint.setTitle("Hint 1"); - codeHint.setExercise(exerciseLite); - codeHint.setProgrammingExerciseTask(programmingExerciseTaskService.getSortedTasks(exercise).getFirst()); - - exerciseHintRepository.save(codeHint); - - codeHint.setTitle("New Title"); - - request.put("/api/programming-exercises/" + codeHint.getExercise().getId() + "/exercise-hints/" + codeHint.getId(), codeHint, HttpStatus.OK); - Optional hintAfterSave = exerciseHintRepository.findById(codeHint.getId()); - assertThat(hintAfterSave).isPresent(); - assertThat(hintAfterSave.get()).isInstanceOf(CodeHint.class); - assertThat((hintAfterSave.get()).getTitle()).isEqualTo("New Title"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void deleteHintAsInstructor() throws Exception { - final ExerciseHint exerciseHint = new ExerciseHint().content("content 4").title("title 4").exercise(exerciseLite); - request.delete("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints/" + 0L, HttpStatus.NOT_FOUND); - request.post("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints", exerciseHint, HttpStatus.CREATED); - final ExerciseHint exerciseHintAfterCreation = exerciseHintRepository.findByExerciseId(exerciseLite.getId()).stream().findAny().orElseThrow(); - request.delete("/api/programming-exercises/" + exerciseHint.getExercise().getId() + "/exercise-hints/" + exerciseHintAfterCreation.getId(), HttpStatus.NO_CONTENT); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void deleteCodeHintAsInstructor() throws Exception { - CodeHint codeHint = new CodeHint(); - codeHint.setTitle("Hint 1"); - codeHint.setExercise(exercise); - exerciseHintRepository.save(codeHint); - - request.delete("/api/programming-exercises/" + codeHint.getExercise().getId() + "/exercise-hints/" + codeHint.getId(), HttpStatus.NO_CONTENT); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGetHintTitleAsInstructor() throws Exception { - // Only user and role matter, so we can re-use the logic - testGetHintTitle(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void testGetHintTitleAsTeachingAssistant() throws Exception { - // Only user and role matter, so we can re-use the logic - testGetHintTitle(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "user1", roles = "USER") - void testGetHintTitleAsUser() throws Exception { - // Only user and role matter, so we can re-use the logic - testGetHintTitle(); - } - - private void testGetHintTitle() throws Exception { - final var hint = new ExerciseHint().title("Test Hint").exercise(exerciseLite); - exerciseHintRepository.save(hint); - - final var title = request.get("/api/programming-exercises/" + hint.getExercise().getId() + "/exercise-hints/" + hint.getId() + "/title", HttpStatus.OK, String.class); - assertThat(title).isEqualTo(hint.getTitle()); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "user1", roles = "USER") - void testGetHintTitleForNonExistingHint() throws Exception { - request.get("/api/programming-exercises/" + exercise.getId() + "/exercise-hints/12312312321/title", HttpStatus.NOT_FOUND, String.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void createHintWithInvalidExerciseIds() throws Exception { - Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(); - var unrelatedExercise = course.getExercises().stream().findFirst().orElseThrow(); - - ExerciseHint exerciseHint = new ExerciseHint(); - exerciseHint.setTitle("Test Title"); - exerciseHint.setExercise(exerciseLite); - - request.post("/api/programming-exercises/" + unrelatedExercise.getId() + "/exercise-hints", exerciseHint, HttpStatus.CONFLICT); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void updateHintWithInvalidExerciseIds() throws Exception { - Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(); - var unrelatedExercise = course.getExercises().stream().findFirst().orElseThrow(); - - exerciseHint.setTitle("New Title"); - - request.put("/api/programming-exercises/" + unrelatedExercise.getId() + "/exercise-hints/" + exerciseHint.getId(), exerciseHint, HttpStatus.CONFLICT); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void getHintTitleWithInvalidExerciseIds() throws Exception { - Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(); - var unrelatedExercise = course.getExercises().stream().findFirst().orElseThrow(); - - request.get("/api/programming-exercises/" + unrelatedExercise.getId() + "/exercise-hints/" + exerciseHint.getId(), HttpStatus.CONFLICT, String.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void getExerciseHintWithInvalidExerciseIds() throws Exception { - Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(); - var unrelatedExercise = course.getExercises().stream().findFirst().orElseThrow(); - - request.get("/api/programming-exercises/" + unrelatedExercise.getId() + "/exercise-hints/" + exerciseHint.getId(), HttpStatus.CONFLICT, String.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void deleteHintWithInvalidExerciseIds() throws Exception { - Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(); - var unrelatedExercise = course.getExercises().stream().findFirst().orElseThrow(); - - request.delete("/api/programming-exercises/" + unrelatedExercise.getId() + "/exercise-hints/" + exerciseHint.getId(), HttpStatus.CONFLICT); - } - - private void addResultWithFailedTestCases(Collection failedTestCases) { - var successfulTestCases = new ArrayList<>(exercise.getTestCases()); - successfulTestCases.removeAll(failedTestCases); - addResultWithSuccessfulTestCases(successfulTestCases); - } - - private void addResultWithSuccessfulTestCases(Collection successfulTestCases) { - var submission = programmingExerciseUtilService.createProgrammingSubmission(studentParticipation, false); - Result result = new Result().participation(submission.getParticipation()).assessmentType(AssessmentType.AUTOMATIC).score(0D).rated(true) - .completionDate(ZonedDateTime.now().plusSeconds(timeOffset++)); - result = resultRepository.save(result); - result.setSubmission(submission); - submission.addResult(result); - programmingSubmissionRepository.save(submission); - - for (ProgrammingExerciseTestCase testCase : exercise.getTestCases()) { - var feedback = new Feedback(); - feedback.setPositive(successfulTestCases.contains(testCase)); - feedback.setTestCase(testCase); - feedback.setVisibility(Visibility.ALWAYS); - feedback.setType(FeedbackType.AUTOMATIC); - participationUtilService.addFeedbackToResult(feedback, result); - } - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintServiceTest.java deleted file mode 100644 index 99b3d357f73f..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintServiceTest.java +++ /dev/null @@ -1,304 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.tum.cit.aet.artemis.assessment.domain.AssessmentType; -import de.tum.cit.aet.artemis.assessment.domain.Feedback; -import de.tum.cit.aet.artemis.assessment.domain.FeedbackType; -import de.tum.cit.aet.artemis.assessment.domain.Result; -import de.tum.cit.aet.artemis.assessment.domain.Visibility; -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.core.domain.User; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHintActivation; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; - -class ExerciseHintServiceTest extends AbstractProgrammingIntegrationIndependentTest { - - private static final String TEST_PREFIX = "exercisehintservice"; - - private ProgrammingExercise exercise; - - private List sortedTasks; - - private List hints; - - private ExerciseHint exerciseHint; - - private User student; - - private ProgrammingExerciseStudentParticipation studentParticipation; - - private int timeOffset = 0; - - @BeforeEach - void initTestCase() { - final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExerciseAndTestCases(); - final ProgrammingExercise programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - - userUtilService.addUsers(TEST_PREFIX, 2, 2, 1, 2); - - student = userRepository.getUserWithGroupsAndAuthorities(TEST_PREFIX + "student1"); - userUtilService.changeUser(TEST_PREFIX + "student1"); - - var activatedTestCases = testCaseRepository.findByExerciseId(programmingExercise.getId()).stream().peek(testCase -> testCase.setActive(true)).toList(); - testCaseRepository.saveAll(activatedTestCases); - exercise = programmingExerciseRepository.findByIdElseThrow(programmingExercise.getId()); - exercise = programmingExerciseUtilService.loadProgrammingExerciseWithEagerReferences(exercise); - programmingExerciseUtilService.addHintsToExercise(exercise); - programmingExerciseUtilService.addTasksToProgrammingExercise(exercise); - - sortedTasks = programmingExerciseTaskService.getSortedTasks(exercise); - - hints = new ArrayList<>(exerciseHintRepository.findByExerciseId(exercise.getId())); - exerciseHint = hints.getFirst(); - exerciseHint.setProgrammingExerciseTask(sortedTasks.getFirst()); - hints.get(1).setProgrammingExerciseTask(sortedTasks.get(1)); - hints.get(2).setProgrammingExerciseTask(sortedTasks.get(2)); - exerciseHintRepository.saveAll(hints); - - studentParticipation = participationUtilService.addStudentParticipationForProgrammingExercise(exercise, student.getLogin()); - } - - @Test - void testGetAvailableExerciseHintsTasksWithoutTestCases() { - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - for (ProgrammingExerciseTask sortedTask : sortedTasks) { - sortedTask.getTestCases().clear(); - taskRepository.save(sortedTask); - } - exercise.setProblemStatement(exercise.getProblemStatement().replaceAll("\\([^()]+\\)", "()")); - programmingExerciseRepository.save(exercise); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).isEmpty(); - } - - @Test - void testGetAvailableExerciseHintsEmpty1() { - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).isEmpty(); - } - - @Test - void testGetAvailableExerciseHintsEmpty2() { - addResultWithFailedTestCases(exercise.getTestCases()); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).isEmpty(); - } - - @Test - void testGetAvailableExerciseHintsEmpty3() { - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithSuccessfulTestCases(exercise.getTestCases()); - addResultWithSuccessfulTestCases(exercise.getTestCases()); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).isEmpty(); - } - - @Test - void testGetAvailableExerciseHintsEmpty4() { - addResultWithSuccessfulTestCases(sortedTasks.getFirst().getTestCases()); - addResultWithSuccessfulTestCases(sortedTasks.getFirst().getTestCases()); - addResultWithFailedTestCases(sortedTasks.get(2).getTestCases()); - addResultWithFailedTestCases(sortedTasks.get(2).getTestCases()); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).isEmpty(); - } - - @Test - void testGetAvailableExerciseHints1() { - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).containsExactly(exerciseHint); - } - - @Test - void testGetAvailableExerciseHints2() { - addResultWithSuccessfulTestCases(sortedTasks.getFirst().getTestCases()); - addResultWithSuccessfulTestCases(sortedTasks.getFirst().getTestCases()); - addResultWithSuccessfulTestCases(sortedTasks.getFirst().getTestCases()); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).containsExactly(hints.get(1)); - } - - @Test - void testGetAvailableExerciseHints3() { - addResultWithSuccessfulTestCases(sortedTasks.get(1).getTestCases()); - addResultWithSuccessfulTestCases(sortedTasks.get(1).getTestCases()); - addResultWithSuccessfulTestCases(sortedTasks.get(1).getTestCases()); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).containsExactly(exerciseHint); - } - - @Test - void testGetAvailableExerciseHints4() { - addResultWithSuccessfulTestCases(sortedTasks.get(2).getTestCases()); - addResultWithSuccessfulTestCases(sortedTasks.get(2).getTestCases()); - addResultWithSuccessfulTestCases(sortedTasks.get(2).getTestCases()); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).containsExactly(exerciseHint); - } - - @Test - void testGetAvailableExerciseHints5() { - addResultWithSuccessfulTestCases(sortedTasks.get(1).getTestCases()); - addResultWithSuccessfulTestCases(sortedTasks.get(2).getTestCases()); - addResultWithSuccessfulTestCases(sortedTasks.get(1).getTestCases()); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).containsExactly(exerciseHint); - } - - @Test - void testGetAvailableExerciseHintsWithZeroThreshold1() { - exerciseHint.setDisplayThreshold((short) 0); - exerciseHintRepository.save(exerciseHint); - addResultWithFailedTestCases(exercise.getTestCases()); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).containsExactly(exerciseHint); - } - - @Test - void testGetAvailableExerciseHintsWithZeroThreshold2() { - exerciseHint.setDisplayThreshold((short) 0); - exerciseHintRepository.save(exerciseHint); - addResultWithSuccessfulTestCases(exercise.getTestCases()); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).containsExactly(exerciseHint); - } - - @Test - void testGetAvailableExerciseHintsWithZeroThreshold3() { - exerciseHint.setDisplayThreshold((short) 0); - exerciseHintRepository.save(exerciseHint); - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithSuccessfulTestCases(sortedTasks.getFirst().getTestCases()); - var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableExerciseHints).containsExactly(exerciseHint); - } - - @Test - void testGetAvailableExerciseHints_skippedTestsConsideredAsNegative() { - // create result with feedbacks with "null" for attribute "positive" - addResultWithSuccessfulTestCases(exercise.getTestCases()); - var results = resultRepository.findAllByParticipationExerciseId(exercise.getId()); - var optionalResult = resultRepository.findWithBidirectionalSubmissionAndFeedbackAndAssessorAndAssessmentNoteAndTeamStudentsById(results.iterator().next().getId()); - assertThat(optionalResult).isPresent(); - - var result = optionalResult.get(); - result.getFeedbacks().forEach(feedback -> feedback.setPositive(null)); - resultRepository.save(result); - - // create results with feedbacks with "false" for attribute "positive" - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - - var availableHints = exerciseHintService.getAvailableExerciseHints(exercise, student); - assertThat(availableHints).containsExactly(exerciseHint); - } - - @Test - void testActivateExerciseHint1() { - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - - assertThat(exerciseHintService.activateHint(hints.getFirst(), student)).isTrue(); - assertThat(exerciseHintService.activateHint(hints.get(1), student)).isFalse(); - assertThat(exerciseHintService.activateHint(hints.get(2), student)).isFalse(); - Set exerciseHintActivations = exerciseHintActivationRepository.findByExerciseAndUserWithExerciseHintRelations(exercise.getId(), student.getId()); - assertThat(exerciseHintActivations).hasSize(1).allMatch(activation -> activation.getExerciseHint().getId().equals(exerciseHint.getId())); - } - - @Test - void testActivateExerciseHint2() { - addResultWithSuccessfulTestCases(sortedTasks.getFirst().getTestCases()); - addResultWithSuccessfulTestCases(sortedTasks.getFirst().getTestCases()); - addResultWithSuccessfulTestCases(sortedTasks.getFirst().getTestCases()); - - assertThat(exerciseHintService.activateHint(hints.getFirst(), student)).isFalse(); - assertThat(exerciseHintService.activateHint(hints.get(1), student)).isTrue(); - assertThat(exerciseHintService.activateHint(hints.get(2), student)).isFalse(); - Set exerciseHintActivations = exerciseHintActivationRepository.findByExerciseAndUserWithExerciseHintRelations(exercise.getId(), student.getId()); - assertThat(exerciseHintActivations).hasSize(1).allMatch(activation -> activation.getExerciseHint().getId().equals(hints.get(1).getId())); - } - - @Test - void testActivateExerciseHint3() { - addResultWithFailedTestCases(sortedTasks.get(2).getTestCases()); - addResultWithFailedTestCases(sortedTasks.get(2).getTestCases()); - addResultWithFailedTestCases(sortedTasks.get(2).getTestCases()); - - assertThat(exerciseHintService.activateHint(hints.getFirst(), student)).isFalse(); - assertThat(exerciseHintService.activateHint(hints.get(1), student)).isFalse(); - assertThat(exerciseHintService.activateHint(hints.get(2), student)).isTrue(); - Set exerciseHintActivations = exerciseHintActivationRepository.findByExerciseAndUserWithExerciseHintRelations(exercise.getId(), student.getId()); - assertThat(exerciseHintActivations).hasSize(1).allMatch(activation -> activation.getExerciseHint().getId().equals(hints.get(2).getId())); - } - - @Test - void testActivateExerciseHint4() { - addResultWithSuccessfulTestCases(exercise.getTestCases()); - addResultWithSuccessfulTestCases(exercise.getTestCases()); - addResultWithSuccessfulTestCases(exercise.getTestCases()); - - assertThat(exerciseHintService.activateHint(hints.getFirst(), student)).isFalse(); - assertThat(exerciseHintService.activateHint(hints.get(1), student)).isFalse(); - assertThat(exerciseHintService.activateHint(hints.get(2), student)).isFalse(); - Set exerciseHintActivations = exerciseHintActivationRepository.findByExerciseAndUserWithExerciseHintRelations(exercise.getId(), student.getId()); - assertThat(exerciseHintActivations).isEmpty(); - } - - @Test - void testActivateExerciseHintTwiceFails() { - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - addResultWithFailedTestCases(exercise.getTestCases()); - - assertThat(exerciseHintService.activateHint(exerciseHint, student)).isTrue(); - assertThat(exerciseHintService.activateHint(exerciseHint, student)).isFalse(); - Set exerciseHintActivations = exerciseHintActivationRepository.findByExerciseAndUserWithExerciseHintRelations(exercise.getId(), student.getId()); - assertThat(exerciseHintActivations).hasSize(1).allMatch(activation -> activation.getExerciseHint().getId().equals(exerciseHint.getId())); - } - - private void addResultWithFailedTestCases(Collection failedTestCases) { - var successfulTestCases = new ArrayList<>(exercise.getTestCases()); - successfulTestCases.removeAll(failedTestCases); - addResultWithSuccessfulTestCases(successfulTestCases); - } - - private void addResultWithSuccessfulTestCases(Collection successfulTestCases) { - var submission = programmingExerciseUtilService.createProgrammingSubmission(studentParticipation, false); - Result result = new Result().participation(submission.getParticipation()).assessmentType(AssessmentType.AUTOMATIC).score(0D).rated(true) - .completionDate(ZonedDateTime.now().plusSeconds(timeOffset++)); - result = resultRepository.save(result); - result.setSubmission(submission); - submission.addResult(result); - programmingSubmissionRepository.save(submission); - - for (ProgrammingExerciseTestCase testCase : exercise.getTestCases()) { - var feedback = new Feedback(); - feedback.setPositive(successfulTestCases.contains(testCase)); - feedback.setTestCase(testCase); - feedback.setVisibility(Visibility.ALWAYS); - feedback.setType(FeedbackType.AUTOMATIC); - participationUtilService.addFeedbackToResult(feedback, result); - } - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/HestiaDatabaseTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/HestiaDatabaseTest.java deleted file mode 100644 index 040547ae9cf9..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/HestiaDatabaseTest.java +++ /dev/null @@ -1,165 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; - -/** - * This class tests the database relations of the Hestia domain models. - * This currently includes ProgrammingExerciseTask, ProgrammingExerciseSolutionEntry and CodeHint. - * It tests if the addition and deletion of these models works as expected. - */ -class HestiaDatabaseTest extends AbstractProgrammingIntegrationIndependentTest { - - private static final String TEST_PREFIX = "hestiadatabase"; - - private Long programmingExerciseId; - - @BeforeEach - void init() { - userUtilService.addUsers(TEST_PREFIX, 2, 2, 0, 2); - final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(); - programmingExerciseId = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class).getId(); - } - - ProgrammingExerciseTask addTaskToProgrammingExercise(String taskName) { - var task = new ProgrammingExerciseTask(); - task.setTaskName(taskName); - task.setExercise(programmingExerciseRepository.getReferenceById(programmingExerciseId)); - task = taskRepository.save(task); - return task; - } - - ProgrammingExerciseSolutionEntry[] addSolutionEntriesToTestCase(int count, ProgrammingExerciseTestCase testCase) { - var solutionEntries = new ProgrammingExerciseSolutionEntry[count]; - for (int i = 0; i < count; i++) { - var solutionEntry = new ProgrammingExerciseSolutionEntry(); - solutionEntry.setTestCase(testCase); - solutionEntry.setCode("Code block 1"); - solutionEntry.setLine(i); - solutionEntry = programmingExerciseSolutionEntryRepository.save(solutionEntry); - solutionEntries[i] = solutionEntry; - } - return solutionEntries; - } - - @Test - void addOneTaskToProgrammingExercise() { - var task = addTaskToProgrammingExercise("Task 1"); - assertThat(taskRepository.findByExerciseIdWithTestCases(programmingExerciseId)).containsExactly(task); - } - - @Test - void deleteProgrammingExerciseWithTask() { - addOneTaskToProgrammingExercise(); - programmingExerciseRepository.deleteById(programmingExerciseId); - assertThat(taskRepository.findByExerciseId(programmingExerciseId)).isEmpty(); - } - - @Test - void addTestCasesWithSolutionEntriesToProgrammingExercise() { - var programmingExercise = programmingExerciseRepository.findByIdElseThrow(programmingExerciseId); - programmingExerciseUtilService.addTestCasesToProgrammingExercise(programmingExercise); - var testCases = testCaseRepository.findByExerciseId(programmingExerciseId); - assertThat(testCases).isNotEmpty(); - for (ProgrammingExerciseTestCase testCase : testCases) { - var solutionEntries = addSolutionEntriesToTestCase(2, testCase); - assertThat(programmingExerciseSolutionEntryRepository.findByTestCaseId(testCase.getId())).containsExactly(solutionEntries); - } - } - - @Test - void deleteProgrammingExerciseWithTestCasesAndSolutionEntries() { - addTestCasesWithSolutionEntriesToProgrammingExercise(); - programmingExerciseRepository.deleteById(programmingExerciseId); - assertThat(testCaseRepository.findByExerciseId(programmingExerciseId)).isEmpty(); - assertThat(programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExerciseId)).isEmpty(); - } - - @Test - void deleteTaskWithTestCases() { - var programmingExercise = programmingExerciseRepository.findByIdElseThrow(programmingExerciseId); - programmingExerciseUtilService.addTestCasesToProgrammingExercise(programmingExercise); - var testCases = testCaseRepository.findByExerciseId(programmingExerciseId); - assertThat(testCases).isNotEmpty(); - var task = addTaskToProgrammingExercise("Task 1"); - task.setTestCases(testCases); - task = taskRepository.save(task); - taskRepository.delete(task); - assertThat(testCaseRepository.findByExerciseId(programmingExerciseId)).isEqualTo(testCases); - } - - @Test - void addCodeHintToProgrammingExercise() { - var programmingExercise = programmingExerciseRepository.findByIdElseThrow(programmingExerciseId); - programmingExerciseUtilService.addTestCasesToProgrammingExercise(programmingExercise); - var testCases = testCaseRepository.findByExerciseId(programmingExerciseId); - assertThat(testCases).isNotEmpty(); - var task = addTaskToProgrammingExercise("Task 1"); - task.setTestCases(testCases); - task = taskRepository.save(task); - Set allSolutionEntries = new HashSet<>(); - for (ProgrammingExerciseTestCase testCase : testCases) { - var solutionEntries = addSolutionEntriesToTestCase(2, testCase); - assertThat(programmingExerciseSolutionEntryRepository.findByTestCaseId(testCase.getId())).containsExactly(solutionEntries); - allSolutionEntries.addAll(List.of(solutionEntries)); - } - var codeHint = (CodeHint) new CodeHint(); - codeHint.setProgrammingExerciseTask(task); - codeHint.setExercise(programmingExercise); - codeHint.setTitle("Code Hint 1"); - codeHint = codeHintRepository.save(codeHint); - for (ProgrammingExerciseSolutionEntry solutionEntry : allSolutionEntries) { - solutionEntry.setCodeHint(codeHint); - programmingExerciseSolutionEntryRepository.save(solutionEntry); - } - codeHint.setSolutionEntries(allSolutionEntries); - codeHint = codeHintRepository.save(codeHint); - task.setExerciseHints(Set.of(codeHint)); - taskRepository.save(task); - assertThat(programmingExerciseSolutionEntryRepository.findByCodeHintId(codeHint.getId())).isEqualTo(allSolutionEntries); - assertThat(codeHintRepository.findByExerciseId(programmingExerciseId)).containsExactly(codeHint); - } - - @Test - void deleteCodeHint() { - addCodeHintToProgrammingExercise(); - var codeHint = codeHintRepository.findByExerciseId(programmingExerciseId).stream().findAny().orElseThrow(); - codeHintRepository.delete(codeHint); - assertThat(taskRepository.findByExerciseId(programmingExerciseId)).hasSize(1); - assertThat(programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExerciseId)).hasSize(6); - } - - @Test - void deleteProgrammingExerciseWithCodeHint() { - addCodeHintToProgrammingExercise(); - programmingExerciseRepository.deleteById(programmingExerciseId); - assertThat(taskRepository.findByExerciseId(programmingExerciseId)).isEmpty(); - assertThat(programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExerciseId)).isEmpty(); - assertThat(codeHintRepository.findByExerciseId(programmingExerciseId)).isEmpty(); - assertThat(testCaseRepository.findByExerciseId(programmingExerciseId)).isEmpty(); - } - - @Test - void deleteTaskWithCodeHint() { - addCodeHintToProgrammingExercise(); - var task = taskRepository.findByExerciseId(programmingExerciseId).stream().findAny().orElseThrow(); - taskRepository.delete(task); - assertThat(codeHintRepository.findByExerciseId(programmingExerciseId)).isEmpty(); - assertThat(testCaseRepository.findByExerciseId(programmingExerciseId)).hasSize(3); - assertThat(programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExerciseId)).hasSize(6); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportIntegrationTest.java deleted file mode 100644 index b896af7ac582..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportIntegrationTest.java +++ /dev/null @@ -1,223 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.ZonedDateTime; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import org.springframework.security.test.context.support.WithMockUser; - -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; -import de.tum.cit.aet.artemis.programming.util.LocalRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; - -/** - * Tests for the ProgrammingExerciseGitDiffReportResource - */ -class ProgrammingExerciseGitDiffReportIntegrationTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { - - private static final String TEST_PREFIX = "progexgitdiffreport"; - - private static final String FILE_NAME = "test.java"; - - private static final String FILE_NAME2 = "test2.java"; - - private final LocalRepository solutionRepo = new LocalRepository("main"); - - private final LocalRepository templateRepo = new LocalRepository("main"); - - private final LocalRepository participationRepo = new LocalRepository("main"); - - private ProgrammingExercise exercise; - - @BeforeEach - void initTestCase() { - Course course = courseUtilService.addEmptyCourse(); - userUtilService.addUsers(TEST_PREFIX, 1, 1, 1, 1); - exercise = ProgrammingExerciseFactory.generateProgrammingExercise(ZonedDateTime.now().minusDays(1), ZonedDateTime.now().plusDays(7), course); - } - - @Override - protected String getTestPrefix() { - return TEST_PREFIX; - } - - @AfterEach - void cleanup() throws Exception { - solutionRepo.resetLocalRepo(); - templateRepo.resetLocalRepo(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void getGitDiffAsAStudent() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "TEST", exercise, templateRepo); - exercise = hestiaUtilTestService.setupSolution(FILE_NAME, "TEST", exercise, solutionRepo); - reportService.updateReport(exercise); - request.get("/api/programming-exercises/" + exercise.getId() + "/diff-report", HttpStatus.FORBIDDEN, ProgrammingExerciseGitDiffReport.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void getGitDiffAsATutor() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "TEST", exercise, templateRepo); - exercise = hestiaUtilTestService.setupSolution(FILE_NAME, "TEST", exercise, solutionRepo); - reportService.updateReport(exercise); - var report = request.get("/api/programming-exercises/" + exercise.getId() + "/diff-report", HttpStatus.OK, ProgrammingExerciseGitDiffReport.class); - assertThat(report).isNotNull(); - assertThat(report.getEntries()).isNull(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void getGitDiffAsAnEditor() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "TEST", exercise, templateRepo); - exercise = hestiaUtilTestService.setupSolution(FILE_NAME, "TEST", exercise, solutionRepo); - reportService.updateReport(exercise); - var report = request.get("/api/programming-exercises/" + exercise.getId() + "/diff-report", HttpStatus.OK, ProgrammingExerciseGitDiffReport.class); - assertThat(report).isNotNull(); - assertThat(report.getEntries()).isNull(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void getGitDiffAsAnInstructor() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "TEST", exercise, templateRepo); - exercise = hestiaUtilTestService.setupSolution(FILE_NAME, "TEST", exercise, solutionRepo); - reportService.updateReport(exercise); - var report = request.get("/api/programming-exercises/" + exercise.getId() + "/diff-report", HttpStatus.OK, ProgrammingExerciseGitDiffReport.class); - assertThat(report).isNotNull(); - assertThat(report.getEntries()).isNull(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void getGitDiffBetweenTemplateAndSubmission() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "ABC", exercise, templateRepo); - participationRepo.configureRepos("participationLocalRepo", "participationOriginRepo"); - var studentLogin = TEST_PREFIX + "student1"; - var submission = hestiaUtilTestService.setupSubmission(FILE_NAME, "TEST", exercise, participationRepo, studentLogin); - var report = request.get("/api/programming-exercises/" + exercise.getId() + "/submissions/" + submission.getId() + "/diff-report-with-template", HttpStatus.OK, - ProgrammingExerciseGitDiffReport.class); - assertThat(report).isNotNull(); - assertThat(report.getEntries()).isNull(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void getGitDiffBetweenTemplateAndSubmissionEditorForbidden() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "ABC", exercise, templateRepo); - participationRepo.configureRepos("participationLocalRepo", "participationOriginRepo"); - var studentLogin = TEST_PREFIX + "student1"; - var submission = hestiaUtilTestService.setupSubmission(FILE_NAME, "TEST", exercise, participationRepo, studentLogin); - request.get("/api/programming-exercises/" + exercise.getId() + "/submissions/" + submission.getId() + "/diff-report-with-template", HttpStatus.FORBIDDEN, - ProgrammingExerciseGitDiffReport.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void getGitDiffReportForCommits() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "ABC", exercise, templateRepo); - participationRepo.configureRepos("participationLocalRepo", "participationOriginRepo"); - var studentLogin = TEST_PREFIX + "student1"; - var submission = hestiaUtilTestService.setupSubmission(FILE_NAME, "TEST", exercise, participationRepo, studentLogin); - var submission2 = hestiaUtilTestService.setupSubmission(FILE_NAME, "TEST2", exercise, participationRepo, studentLogin); - var report = request.get("/api/programming-exercises/" + exercise.getId() + "/commits/" + submission.getCommitHash() + "/diff-report/" + submission2.getCommitHash() - + "?participationId=" + submission.getParticipation().getId(), HttpStatus.OK, ProgrammingExerciseGitDiffReport.class); - assertThat(report).isNotNull(); - assertThat(report.getEntries()).hasSize(1); - var entry = report.getEntries().stream().findAny().orElseThrow(); - assertThat(entry.getPreviousFilePath()).isEqualTo(FILE_NAME); - assertThat(entry.getPreviousStartLine()).isEqualTo(1); - assertThat(entry.getPreviousLineCount()).isEqualTo(1); - assertThat(entry.getFilePath()).isEqualTo(FILE_NAME); - assertThat(entry.getStartLine()).isEqualTo(1); - assertThat(entry.getLineCount()).isEqualTo(1); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void getGitDiffReportForCommitsWithRenamedFile() throws Exception { - String fileContent = "content\ncontent\ncontent\ncontent"; - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, fileContent, exercise, templateRepo); - participationRepo.configureRepos("participationLocalRepo", "participationOriginRepo"); - var studentLogin = TEST_PREFIX + "student1"; - var submission = hestiaUtilTestService.setupSubmission(FILE_NAME, fileContent, exercise, participationRepo, studentLogin); - // Simulate a renaming by deleting the file and creating a new one with the same content. Git will track this as long as the content is similar enough. - var submission2 = hestiaUtilTestService.deleteFileAndSetupSubmission(FILE_NAME, FILE_NAME2, fileContent, exercise, participationRepo, studentLogin); - var report = request.get("/api/programming-exercises/" + exercise.getId() + "/commits/" + submission.getCommitHash() + "/diff-report/" + submission2.getCommitHash() - + "?participationId=" + submission.getParticipation().getId(), HttpStatus.OK, ProgrammingExerciseGitDiffReport.class); - var entries = report.getEntries(); - assertThat(entries.size()).isEqualTo(1); - ProgrammingExerciseGitDiffEntry entry = entries.iterator().next(); - assertThat(entry.getPreviousFilePath()).isEqualTo(FILE_NAME); - assertThat(entry.getFilePath()).isEqualTo(FILE_NAME2); - assertThat(entry.isEmpty()).isTrue(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void getGitDiffReportForCommitsThrowsConflictException() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "ABC", exercise, templateRepo); - var wrongExerciseId = exercise.getId() + 1; - participationRepo.configureRepos("participationLocalRepo", "participationOriginRepo"); - var studentLogin = TEST_PREFIX + "student1"; - var submission = hestiaUtilTestService.setupSubmission(FILE_NAME, "TEST", exercise, participationRepo, studentLogin); - var submission2 = hestiaUtilTestService.setupSubmission(FILE_NAME, "TEST2", exercise, participationRepo, studentLogin); - request.get("/api/programming-exercises/" + wrongExerciseId + "/commits/" + submission.getCommitHash() + "/diff-report/" + submission2.getCommitHash() + "?participationId=" - + submission.getParticipation().getId(), HttpStatus.CONFLICT, ProgrammingExerciseGitDiffReport.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void getGitDiffReportForCommitsForbiddenIfNotParticipationOwner() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "ABC", exercise, templateRepo); - participationRepo.configureRepos("participationLocalRepo", "participationOriginRepo"); - // Create a submission for student2 and try to access it with student1 - var studentLogin = TEST_PREFIX + "instructor1"; - var submission = hestiaUtilTestService.setupSubmission(FILE_NAME, "TEST", exercise, participationRepo, studentLogin); - var submission2 = hestiaUtilTestService.setupSubmission(FILE_NAME, "TEST2", exercise, participationRepo, studentLogin); - request.get("/api/programming-exercises/" + exercise.getId() + "/commits/" + submission.getCommitHash() + "/diff-report/" + submission2.getCommitHash() - + "?participationId=" + submission.getParticipation().getId(), HttpStatus.FORBIDDEN, ProgrammingExerciseGitDiffReport.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void getGitDiffBetweenTwoSubmissions() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "ABC", exercise, templateRepo); - participationRepo.configureRepos("participationLocalRepo", "participationOriginRepo"); - var studentLogin = TEST_PREFIX + "student1"; - var submission = hestiaUtilTestService.setupSubmission(FILE_NAME, "TEST", exercise, participationRepo, studentLogin); - var submission2 = hestiaUtilTestService.setupSubmission(FILE_NAME, "TEST2", exercise, participationRepo, studentLogin); - var report = request.get("/api/programming-exercises/" + exercise.getId() + "/submissions/" + submission.getId() + "/diff-report/" + submission2.getId(), HttpStatus.OK, - ProgrammingExerciseGitDiffReport.class); - assertThat(report).isNotNull(); - assertThat(report.getEntries()).hasSize(1); - var entry = report.getEntries().stream().findAny().orElseThrow(); - assertThat(entry.getPreviousFilePath()).isEqualTo(FILE_NAME); - assertThat(entry.getPreviousStartLine()).isEqualTo(1); - assertThat(entry.getPreviousLineCount()).isEqualTo(1); - assertThat(entry.getFilePath()).isEqualTo(FILE_NAME); - assertThat(entry.getStartLine()).isEqualTo(1); - assertThat(entry.getLineCount()).isEqualTo(1); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void getGitDiffBetweenTwoSubmissionsEditorForbidden() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "ABC", exercise, templateRepo); - participationRepo.configureRepos("participationLocalRepo", "participationOriginRepo"); - var studentLogin = TEST_PREFIX + "student1"; - var submission = hestiaUtilTestService.setupSubmission(FILE_NAME, "TEST", exercise, participationRepo, studentLogin); - var submission2 = hestiaUtilTestService.setupSubmission(FILE_NAME2, "TEST2", exercise, participationRepo, studentLogin); - request.get("/api/programming-exercises/" + exercise.getId() + "/submissions/" + submission.getId() + "/diff-report/" + submission2.getId(), HttpStatus.FORBIDDEN, - ProgrammingExerciseGitDiffReport.class); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportServiceTest.java deleted file mode 100644 index a79c8880e1fb..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportServiceTest.java +++ /dev/null @@ -1,179 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Comparator; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.security.test.context.support.WithMockUser; - -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; -import de.tum.cit.aet.artemis.programming.util.LocalRepository; - -/** - * Tests for the ProgrammingExerciseGitDiffReportService - */ -class ProgrammingExerciseGitDiffReportServiceTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { - - private static final String TEST_PREFIX = "progexgitdiffreportservice"; - - private static final String FILE_NAME = "test.java"; - - private final LocalRepository solutionRepo = new LocalRepository("main"); - - private final LocalRepository templateRepo = new LocalRepository("main"); - - private ProgrammingExercise exercise; - - @Override - protected String getTestPrefix() { - return TEST_PREFIX; - } - - @BeforeEach - void initTestCase() { - userUtilService.addUsers(TEST_PREFIX, 1, 1, 1, 1); - final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(); - exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - } - - @AfterEach - void cleanup() throws IOException { - templateRepo.resetLocalRepo(); - solutionRepo.resetLocalRepo(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void updateGitDiffNoChanges() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "Line 1\nLine 2", exercise, templateRepo); - exercise = hestiaUtilTestService.setupSolution(FILE_NAME, "Line 1\nLine 2", exercise, solutionRepo); - var report = reportService.updateReport(exercise); - assertThat(report.getEntries()).isNullOrEmpty(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void updateGitDiffAppendLine1() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "Line 1\nLine 2", exercise, templateRepo); - exercise = hestiaUtilTestService.setupSolution(FILE_NAME, "Line 1\nLine 2\nLine 3\n", exercise, solutionRepo); - var report = reportService.updateReport(exercise); - assertThat(report.getEntries()).hasSize(1); - var entry = report.getEntries().stream().findFirst().orElseThrow(); - assertThat(entry.getPreviousStartLine()).isEqualTo(2); - assertThat(entry.getStartLine()).isEqualTo(2); - assertThat(entry.getPreviousLineCount()).isEqualTo(1); - assertThat(entry.getLineCount()).isEqualTo(2); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void updateGitDiffAppendLine2() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "Line 1\nLine 2\n", exercise, templateRepo); - exercise = hestiaUtilTestService.setupSolution(FILE_NAME, "Line 1\nLine 2\nLine 3\n", exercise, solutionRepo); - var report = reportService.updateReport(exercise); - assertThat(report.getEntries()).hasSize(1); - var entry = report.getEntries().stream().findFirst().orElseThrow(); - assertThat(entry.getPreviousStartLine()).isNull(); - assertThat(entry.getStartLine()).isEqualTo(3); - assertThat(entry.getPreviousLineCount()).isNull(); - assertThat(entry.getLineCount()).isEqualTo(1); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void updateGitDiffAddToEmptyFile() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "", exercise, templateRepo); - exercise = hestiaUtilTestService.setupSolution(FILE_NAME, "Line 1\nLine 2", exercise, solutionRepo); - var report = reportService.updateReport(exercise); - assertThat(report.getEntries()).hasSize(1); - var entry = report.getEntries().stream().findFirst().orElseThrow(); - assertThat(entry.getPreviousStartLine()).isNull(); - assertThat(entry.getStartLine()).isEqualTo(1); - assertThat(entry.getPreviousLineCount()).isNull(); - assertThat(entry.getLineCount()).isEqualTo(2); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void updateGitDiffClearFile() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "Line 1\nLine 2", exercise, templateRepo); - exercise = hestiaUtilTestService.setupSolution(FILE_NAME, "", exercise, solutionRepo); - var report = reportService.updateReport(exercise); - assertThat(report.getEntries()).hasSize(1); - var entry = report.getEntries().stream().findFirst().orElseThrow(); - assertThat(entry.getPreviousStartLine()).isEqualTo(1); - assertThat(entry.getStartLine()).isNull(); - assertThat(entry.getPreviousLineCount()).isEqualTo(2); - assertThat(entry.getLineCount()).isNull(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void updateGitDiffDoubleModify() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "L1\nL2\nL3\nL4", exercise, templateRepo); - exercise = hestiaUtilTestService.setupSolution(FILE_NAME, "L1\nL2a\nL3\nL4a", exercise, solutionRepo); - var report = reportService.updateReport(exercise); - assertThat(report.getEntries()).hasSize(2); - var entries = new ArrayList<>(report.getEntries()); - entries.sort(Comparator.comparing(ProgrammingExerciseGitDiffEntry::getStartLine)); - assertThat(entries.getFirst().getPreviousStartLine()).isEqualTo(2); - assertThat(entries.getFirst().getStartLine()).isEqualTo(2); - assertThat(entries.getFirst().getPreviousLineCount()).isEqualTo(1); - assertThat(entries.getFirst().getLineCount()).isEqualTo(1); - - assertThat(entries.get(1).getPreviousStartLine()).isEqualTo(4); - assertThat(entries.get(1).getStartLine()).isEqualTo(4); - assertThat(entries.get(1).getPreviousLineCount()).isEqualTo(1); - assertThat(entries.get(1).getLineCount()).isEqualTo(1); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void gitDiffWhitespace() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, " ", exercise, templateRepo); - exercise = hestiaUtilTestService.setupSolution(FILE_NAME, "\t", exercise, solutionRepo); - var report = reportService.updateReport(exercise); - assertThat(report.getEntries()).hasSize(0); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void updateGitDiffReuseExisting() throws Exception { - exercise = hestiaUtilTestService.setupTemplate(FILE_NAME, "Line 1\nLine 2", exercise, templateRepo); - exercise = hestiaUtilTestService.setupSolution(FILE_NAME, "Line 1\nLine 2\nLine 3\n", exercise, solutionRepo); - var report1 = reportService.updateReport(exercise); - assertThat(report1.getEntries()).hasSize(1); - var report2 = reportService.updateReport(exercise); - assertThat(report2.getEntries()).hasSize(1); - assertThat(report1.getId()).isEqualTo(report2.getId()); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void ensureDeletionOfDuplicateReports() { - var report1 = new ProgrammingExerciseGitDiffReport(); - report1.setProgrammingExercise(exercise); - report1.setTemplateRepositoryCommitHash("123"); - report1.setSolutionRepositoryCommitHash("456"); - reportRepository.save(report1); - var report2 = new ProgrammingExerciseGitDiffReport(); - report2.setProgrammingExercise(exercise); - report2.setTemplateRepositoryCommitHash("123"); - report2.setSolutionRepositoryCommitHash("789"); - report2 = reportRepository.save(report2); - - assertThat(reportRepository.findByProgrammingExerciseId(exercise.getId())).hasSize(2); - var returnedReport = reportService.getOrCreateReportOfExercise(exercise); - assertThat(returnedReport).isEqualTo(report2); - assertThat(reportRepository.findByProgrammingExerciseId(exercise.getId())).hasSize(1); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseSolutionEntryIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseSolutionEntryIntegrationTest.java deleted file mode 100644 index 17d326cfc221..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseSolutionEntryIntegrationTest.java +++ /dev/null @@ -1,245 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import org.springframework.security.test.context.support.WithMockUser; - -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; - -class ProgrammingExerciseSolutionEntryIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { - - private static final String TEST_PREFIX = "progexsolutionentry"; - - private ProgrammingExercise programmingExercise; - - private CodeHint codeHint; - - @BeforeEach - void initTestCase() { - var course = programmingExerciseUtilService.addCourseWithOneProgrammingExerciseAndTestCases(); - userUtilService.addUsers(TEST_PREFIX, 2, 2, 1, 2); - - programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - Set testCases = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()); - - codeHint = new CodeHint(); - codeHint.setExercise(programmingExercise); - codeHint.setTitle("Code Hint Title"); - codeHint.setContent("Code Hint Content"); - codeHintRepository.save(codeHint); - - for (ProgrammingExerciseTestCase testCase : testCases) { - var solutionEntry = new ProgrammingExerciseSolutionEntry(); - solutionEntry.setTestCase(testCase); - solutionEntry.setPreviousCode("No code"); - solutionEntry.setCode("Some code"); - solutionEntry.setPreviousLine(1); - solutionEntry.setCodeHint(codeHint); - solutionEntry.setLine(1); - solutionEntry.setFilePath("code.java"); - programmingExerciseSolutionEntryRepository.save(solutionEntry); - } - ProgrammingExerciseTask task = new ProgrammingExerciseTask(); - task.setExercise(programmingExercise); - task.setTaskName("Task"); - task.setTestCases(new HashSet<>(testCases)); - task = taskRepository.save(task); - codeHint.setProgrammingExerciseTask(task); - codeHintRepository.save(codeHint); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void testGetSolutionEntryById() throws Exception { - Long entryId = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().findFirst().orElseThrow().getId(); - ProgrammingExerciseSolutionEntry expectedSolutionEntry = programmingExerciseSolutionEntryRepository.findByIdWithTestCaseAndProgrammingExerciseElseThrow(entryId); - final var actualSolutionEntry = request.get("/api/programming-exercises/" + programmingExercise.getId() + "/solution-entries/" + expectedSolutionEntry.getId(), - HttpStatus.OK, ProgrammingExerciseSolutionEntry.class); - assertThat(actualSolutionEntry).isEqualTo(expectedSolutionEntry); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void testGetSolutionEntryByIdAsStudent() throws Exception { - Long entryId = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().findFirst().orElseThrow().getId(); - ProgrammingExerciseSolutionEntry expectedSolutionEntry = programmingExerciseSolutionEntryRepository.findByIdWithTestCaseAndProgrammingExerciseElseThrow(entryId); - request.get("/api/programming-exercises/" + programmingExercise.getId() + "/solution-entries/" + expectedSolutionEntry.getId(), HttpStatus.FORBIDDEN, - ProgrammingExerciseSolutionEntry.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void testGetSolutionEntryByIdWithInvalidExerciseIdAsStudent() throws Exception { - Long entryId = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().findFirst().orElseThrow().getId(); - request.get("/api/programming-exercises/" + Long.MAX_VALUE + "/solution-entries/" + entryId, HttpStatus.FORBIDDEN, ProgrammingExerciseSolutionEntry.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void testGetSolutionEntriesByCodeHintId() throws Exception { - final Set solutionEntries = new HashSet<>( - request.getList("/api/programming-exercises/" + programmingExercise.getId() + "/code-hints/" + codeHint.getId() + "/solution-entries", HttpStatus.OK, - ProgrammingExerciseSolutionEntry.class)); - assertThat(solutionEntries).isEqualTo(programmingExerciseSolutionEntryRepository.findByCodeHintId(codeHint.getId())); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void testGetSolutionEntriesByTestCaseId() throws Exception { - ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); - final var solutionEntries = new HashSet<>( - request.getList("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries", HttpStatus.OK, - ProgrammingExerciseSolutionEntry.class)); - assertThat(solutionEntries).isEqualTo(testCase.getSolutionEntries()); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void testGetAllSolutionEntries() throws Exception { - var existingEntries = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); - final var receivedEntries = request.getList("/api/programming-exercises/" + programmingExercise.getId() + "/solution-entries", HttpStatus.OK, - ProgrammingExerciseSolutionEntry.class); - assertThat(receivedEntries).containsExactlyElementsOf(existingEntries); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void testDeleteSolutionEntry() throws Exception { - ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); - Long entryId = testCase.getSolutionEntries().stream().findFirst().orElseThrow().getId(); - request.delete("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries/" + entryId, HttpStatus.NO_CONTENT); - assertThat(programmingExerciseSolutionEntryRepository.findById(entryId)).isEmpty(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "STUDENT") - void testDeleteSolutionEntryAsStudent() throws Exception { - ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); - Long entryId = testCase.getSolutionEntries().stream().findFirst().orElseThrow().getId(); - request.delete("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries/" + entryId, HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void testDeleteSolutionEntryAsTutor() throws Exception { - ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); - Long entryId = testCase.getSolutionEntries().stream().findFirst().orElseThrow().getId(); - request.delete("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries/" + entryId, HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void testDeleteAllSolutionEntriesForExercise() throws Exception { - request.delete("/api/programming-exercises/" + programmingExercise.getId() + "/solution-entries", HttpStatus.NO_CONTENT); - assertThat(programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExercise.getId())).hasSize(0); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void testUpdateSolutionEntry() throws Exception { - ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); - ProgrammingExerciseSolutionEntry entry = testCase.getSolutionEntries().stream().findFirst().orElseThrow(); - Long entryId = entry.getId(); - String updatedFilePath = "NewPath.java"; - entry.setFilePath(updatedFilePath); - - request.put("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries/" + entryId, entry, HttpStatus.OK); - Optional entryAfterUpdate = programmingExerciseSolutionEntryRepository.findById(entryId); - assertThat(entryAfterUpdate).isPresent(); - assertThat(entryAfterUpdate.get().getFilePath()).isEqualTo(updatedFilePath); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void testUpdateSolutionEntryWithInvalidId() throws Exception { - ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); - ProgrammingExerciseSolutionEntry entry = testCase.getSolutionEntries().stream().findFirst().orElseThrow(); - Long entryId = entry.getId(); - String updatedFilePath = "NewPath.java"; - entry.setFilePath(updatedFilePath); - - request.put("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries/" + entryId, entry, HttpStatus.OK); - Optional entryAfterUpdate = programmingExerciseSolutionEntryRepository.findById(entryId); - assertThat(entryAfterUpdate).isPresent(); - assertThat(entryAfterUpdate.get().getFilePath()).isEqualTo(updatedFilePath); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "STUDENT") - void testUpdateSolutionEntryAsStudent() throws Exception { - ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); - ProgrammingExerciseSolutionEntry entry = testCase.getSolutionEntries().stream().findFirst().orElseThrow(); - Long entryId = entry.getId(); - - request.put("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries/" + entryId, entry, HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void testUpdateSolutionEntryAsTutor() throws Exception { - ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); - ProgrammingExerciseSolutionEntry entry = testCase.getSolutionEntries().stream().findFirst().orElseThrow(); - Long entryId = entry.getId(); - - request.put("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries/" + entryId, entry, HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "STUDENT") - void testCreateStructuralSolutionEntriesAsStudent() throws Exception { - request.post("/api/programming-exercises/" + programmingExercise.getId() + "/structural-solution-entries", null, HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void testCreateStructuralSolutionEntriesAsTutor() throws Exception { - request.post("/api/programming-exercises/" + programmingExercise.getId() + "/structural-solution-entries", null, HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void testCreateStructuralSolutionEntriesAsEditor() throws Exception { - request.postWithoutLocation("/api/programming-exercises/" + programmingExercise.getId() + "/structural-solution-entries", null, HttpStatus.OK, null); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testCreateStructuralSolutionEntriesAsInstructor() throws Exception { - request.postWithoutLocation("/api/programming-exercises/" + programmingExercise.getId() + "/structural-solution-entries", null, HttpStatus.OK, null); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testCreateManualSolutionEntry() throws Exception { - programmingExerciseSolutionEntryRepository.deleteAll(); - - var manualEntry = new ProgrammingExerciseSolutionEntry(); - manualEntry.setCode("abc"); - manualEntry.setLine(1); - manualEntry.setFilePath("src/de/tum/in/ase/BubbleSort.java"); - - var testCase = testCaseRepository.findByExerciseId(programmingExercise.getId()).stream().findFirst().orElseThrow(); - manualEntry.setTestCase(testCase); - - request.postWithoutLocation("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries", manualEntry, - HttpStatus.CREATED, null); - - var savedEntries = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); - assertThat(savedEntries).hasSize(1); - var createdEntry = savedEntries.iterator().next(); - assertThat(createdEntry).usingRecursiveComparison().ignoringActualNullFields().isEqualTo(createdEntry); - assertThat(createdEntry.getTestCase().getId()).isEqualTo(testCase.getId()); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskIntegrationTest.java deleted file mode 100644 index 55f7d322a023..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskIntegrationTest.java +++ /dev/null @@ -1,222 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import org.springframework.security.test.context.support.WithMockUser; - -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.core.domain.DomainObject; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; - -class ProgrammingExerciseTaskIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { - - private static final String TEST_PREFIX = "progextask"; - - private ProgrammingExercise programmingExercise; - - private Set testCases; - - @BeforeEach - void initTestCases() { - userUtilService.addUsers(TEST_PREFIX, 1, 1, 1, 1); - - final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExerciseAndSpecificTestCases(); - programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - this.testCases = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()); - for (ProgrammingExerciseTestCase testCase : testCases) { - var solutionEntry = new ProgrammingExerciseSolutionEntry(); - solutionEntry.setTestCase(testCase); - solutionEntry.setPreviousCode("No code"); - solutionEntry.setCode("Some code"); - solutionEntry.setPreviousLine(1); - solutionEntry.setCodeHint(null); - solutionEntry.setLine(1); - solutionEntry.setFilePath("code.java"); - programmingExerciseSolutionEntryRepository.save(solutionEntry); - } - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void testDeletionAsStudent() throws Exception { - request.delete("/api/programming-exercises/" + programmingExercise.getId() + "/tasks", HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void testDeletionAsTutor() throws Exception { - request.delete("/api/programming-exercises/" + programmingExercise.getId() + "/tasks", HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") - void testDeletionAsEditor() throws Exception { - request.delete("/api/programming-exercises/" + programmingExercise.getId() + "/tasks", HttpStatus.NO_CONTENT); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testDeleteAllTasksAndSolutionEntriesForProgrammingExercise() throws Exception { - Set solutionEntryIdsBeforeDeleting = testCases.stream().map(ProgrammingExerciseTestCase::getSolutionEntries).flatMap(Collection::stream).map(DomainObject::getId) - .collect(Collectors.toSet()); - - ProgrammingExerciseTask task = new ProgrammingExerciseTask(); - task.setExercise(programmingExercise); - task.setTaskName("Task"); - task.setTestCases(new HashSet<>(testCases)); - taskRepository.save(task); - - request.delete("/api/programming-exercises/" + programmingExercise.getId() + "/tasks", HttpStatus.NO_CONTENT); - assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).isEmpty(); - assertThat(programmingExerciseSolutionEntryRepository.findAllById(solutionEntryIdsBeforeDeleting)).isEmpty(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void testTaskExtractionAsStudent() throws Exception { - request.get("/api/programming-exercises/" + programmingExercise.getId() + "/tasks", HttpStatus.FORBIDDEN, Set.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void testTaskExtractionForProgrammingExercise() throws Exception { - String taskName1 = "Implement Bubble Sort"; - String taskName2 = "Implement Policy and Context"; - programmingExercise.setProblemStatement(""" - # Sorting with the Strategy Pattern - - In this exercise, we want to implement sorting algorithms and choose them based on runtime specific variables. - - ### Part 1: Sorting - - First, we need to implement two sorting algorithms, in this case `MergeSort` and `BubbleSort`. - - **You have the following tasks:** - - 1. [task][%s](testClass[BubbleSort]) - Implement the class `BubbleSort`. - 2. [task][%s](testMethods[Context],testMethods[Policy]) - Implement the classes `Context` and `Policy`. Make sure to follow..""".formatted(taskName1, taskName2)); - programmingExerciseRepository.save(programmingExercise); - programmingExerciseTaskService.updateTasksFromProblemStatement(programmingExercise); - - request.get("/api/programming-exercises/" + programmingExercise.getId() + "/tasks", HttpStatus.OK, Set.class); - List extractedTasks = taskRepository.findByExerciseIdWithTestCaseAndSolutionEntriesElseThrow(programmingExercise.getId()); - Optional task1Optional = extractedTasks.stream().filter(task -> task.getTaskName().equals(taskName1)).findFirst(); - Optional task2Optional = extractedTasks.stream().filter(task -> task.getTaskName().equals(taskName2)).findFirst(); - assertThat(task1Optional).isPresent(); - assertThat(task2Optional).isPresent(); - ProgrammingExerciseTask task1 = task1Optional.get(); - ProgrammingExerciseTask task2 = task2Optional.get(); - - Set expectedTestCasesForTask1 = new HashSet<>(); - expectedTestCasesForTask1.add(testCases.stream().filter(testCase -> "testClass[BubbleSort]".equals(testCase.getTestName())).findFirst().orElseThrow()); - Set expectedTestCasesForTask2 = new HashSet<>(); - expectedTestCasesForTask2.add(testCases.stream().filter(testCase -> "testMethods[Context]".equals(testCase.getTestName())).findFirst().orElseThrow()); - expectedTestCasesForTask2.add(testCases.stream().filter(testCase -> "testMethods[Policy]".equals(testCase.getTestName())).findFirst().orElseThrow()); - assertThat(task1.getTestCases()).isEqualTo(expectedTestCasesForTask1); - assertThat(task2.getTestCases()).isEqualTo(expectedTestCasesForTask2); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void testTaskExtractionForEmptyProblemStatement() throws Exception { - programmingExercise.setProblemStatement(""); - programmingExercise = programmingExerciseRepository.save(programmingExercise); - - request.get("/api/programming-exercises/" + programmingExercise.getId() + "/tasks", HttpStatus.OK, Set.class); - - assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).isEmpty(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void testGetTasksWithUnassignedTestCases_NoTasks() throws Exception { - var response = request.getList("/api/programming-exercises/" + programmingExercise.getId() + "/tasks-with-unassigned-test-cases", HttpStatus.OK, - ProgrammingExerciseTask.class); - - // No tasks available -> all tests in one "unassigned" group - assertThat(response).hasSize(1); - var unassigned = response.getFirst(); - assertThat(unassigned.getTaskName()).isEqualTo("Not assigned to task"); - assertThat(unassigned.getTestCases()).hasSize(3); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void testGetTasksWithUnassignedTestCases_AllTasksAssigned() throws Exception { - String taskName1 = "Implement Bubble Sort"; - String taskName2 = "Implement Policy and Context"; - programmingExercise.setProblemStatement(""" - # Sorting with the Strategy Pattern - - In this exercise, we want to implement sorting algorithms and choose them based on runtime specific variables. - - ### Part 1: Sorting - - First, we need to implement two sorting algorithms, in this case `MergeSort` and `BubbleSort`. - - **You have the following tasks:** - - 1. [task][%s](testClass[BubbleSort]) - Implement the class `BubbleSort`. - 2. [task][%s](testMethods[Context],testMethods[Policy]) - Implement the classes `Context` and `Policy`. Make sure to follow.. - """.formatted(taskName1, taskName2)); - programmingExerciseRepository.save(programmingExercise); - programmingExerciseTaskService.updateTasksFromProblemStatement(programmingExercise); - - var response = request.getList("/api/programming-exercises/" + programmingExercise.getId() + "/tasks-with-unassigned-test-cases", HttpStatus.OK, - ProgrammingExerciseTask.class); - - // 2 tasks, all test cases distributed across the tasks -> no unassigned - assertThat(response).hasSize(2); - var bubbleSort = response.stream().filter(task -> taskName1.equals(task.getTaskName())).findFirst().orElseThrow(); - var context = response.stream().filter(task -> taskName2.equals(task.getTaskName())).findFirst().orElseThrow(); - - assertThat(bubbleSort.getTestCases()).hasSize(1).allMatch(tc -> "testClass[BubbleSort]".equals(tc.getTestName())); - assertThat(context.getTestCases()).hasSize(2).anyMatch(tc -> "testMethods[Context]".equals(tc.getTestName())) - .anyMatch(tc -> "testMethods[Policy]".equals(tc.getTestName())); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void testGetTasksWithUnassignedTestCases_Mixed() throws Exception { - String taskName = "Implement Bubble Sort"; - programmingExercise.setProblemStatement("[task][%s](testClass[BubbleSort])".formatted(taskName)); - programmingExerciseRepository.save(programmingExercise); - programmingExerciseTaskService.updateTasksFromProblemStatement(programmingExercise); - - var response = request.getList("/api/programming-exercises/" + programmingExercise.getId() + "/tasks-with-unassigned-test-cases", HttpStatus.OK, - ProgrammingExerciseTask.class); - - // 1 task, 1 unassigned - assertThat(response).hasSize(2); - var bubbleSort = response.stream().filter(task -> taskName.equals(task.getTaskName())).findFirst().orElseThrow(); - var unassigned = response.stream().filter(task -> "Not assigned to task".equals(task.getTaskName())).findFirst().orElseThrow(); - - assertThat(bubbleSort.getTestCases()).hasSize(1).allMatch(tc -> "testClass[BubbleSort]".equals(tc.getTestName())); - assertThat(unassigned.getTestCases()).hasSize(2).anyMatch(tc -> "testMethods[Context]".equals(tc.getTestName())) - .anyMatch(tc -> "testMethods[Policy]".equals(tc.getTestName())); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void testGetTasksWithUnassignedTestCases_AsStudent() throws Exception { - request.get("/api/programming-exercises/" + programmingExercise.getId() + "/tasks-with-unassigned-test-cases", HttpStatus.FORBIDDEN, Set.class); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskServiceTest.java deleted file mode 100644 index a322fa44ba44..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskServiceTest.java +++ /dev/null @@ -1,402 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.security.test.context.support.WithMockUser; - -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.core.domain.DomainObject; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; - -class ProgrammingExerciseTaskServiceTest extends AbstractProgrammingIntegrationIndependentTest { - - private static final String TEST_PREFIX = "progextaskservice"; - - private ProgrammingExercise programmingExercise; - - @BeforeEach - void init() { - userUtilService.addUsers(TEST_PREFIX, 1, 1, 1, 1); - - final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExerciseAndSpecificTestCases(); - programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - updateProblemStatement(""" - [task][Task 1](testClass[BubbleSort]) - [task][Task 2](testMethods[Context]) - """); - } - - private void updateProblemStatement(String problemStatement) { - programmingExercise.setProblemStatement(problemStatement); - programmingExercise = programmingExerciseRepository.save(programmingExercise); - programmingExerciseTaskService.updateTasksFromProblemStatement(programmingExercise); - } - - @Test - void testNewExercise() { - assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).hasSize(2); - var tasks = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); - assertThat(tasks).hasSize(2).anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 1", "testClass[BubbleSort]")) - .anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 2", "testMethods[Context]")); - } - - @Test - void testAddTask() { - var previousTaskIds = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().map(ProgrammingExerciseTask::getId).collect(Collectors.toSet()); - - updateProblemStatement(""" - [task][Task 1](testClass[BubbleSort]) - [task][Task 2](testMethods[Context]) - [task][Task 3](testMethods[Policy]) - """); - assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).hasSize(3); - var tasks = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); - assertThat(tasks).hasSize(3).anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 1", "testClass[BubbleSort]")) - .anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 2", "testMethods[Context]")) - .anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 3", "testMethods[Policy]")); - - // Test that the other tasks were not removed and re-added. - var newTaskIds = tasks.stream().map(ProgrammingExerciseTask::getId).collect(Collectors.toSet()); - assertThat(newTaskIds).containsAll(previousTaskIds); - } - - @Test - void testRemoveAllTasks() { - updateProblemStatement("Empty"); - assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).isEmpty(); - } - - @Test - void testReduceToOneTask() { - updateProblemStatement("[task][Task 1](testClass[BubbleSort],testMethods[Context], testMethods[Policy])"); - assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).hasSize(1); - var tasks = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); - assertThat(tasks).hasSize(1); - var task = tasks.stream().findFirst().orElseThrow(); - assertThat(task.getTaskName()).isEqualTo("Task 1"); - assertThat(task.getTestCases()).hasSize(3); - var expectedTestCaseNames = Set.of("testClass[BubbleSort]", "testMethods[Context]", "testMethods[Policy]"); - var actualTestCaseNames = task.getTestCases().stream().map(ProgrammingExerciseTestCase::getTestName).collect(Collectors.toSet()); - assertThat(actualTestCaseNames).isEqualTo(expectedTestCaseNames); - } - - /** - * Tests that renaming a task does not remove and read the task, but instead updates it - */ - @Test - void testRenameTask() { - var previousTaskIds = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().map(ProgrammingExerciseTask::getId).collect(Collectors.toSet()); - - updateProblemStatement(""" - [task][Task 1a](testClass[BubbleSort]) - [task][Task 2](testMethods[Context]) - """); - - assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).hasSize(2); - var tasks = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); - - var newTaskIds = tasks.stream().map(ProgrammingExerciseTask::getId).collect(Collectors.toSet()); - assertThat(previousTaskIds).isEqualTo(newTaskIds); - - assertThat(taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId())).isEqualTo(tasks); - - assertThat(tasks).anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 1a", "testClass[BubbleSort]")) - .anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 2", "testMethods[Context]")); - } - - /** - * Tests that not changing any tasks in the problem statement will not update any tasks - */ - @Test - void testNoChanges() { - var previousTaskIds = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().map(ProgrammingExerciseTask::getId).collect(Collectors.toSet()); - - updateProblemStatement(""" - Test - [task][Task 1](testClass[BubbleSort]) - [task][Task 2](testMethods[Context]) - """); - - assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).hasSize(2); - - var newTaskIds = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().map(ProgrammingExerciseTask::getId).collect(Collectors.toSet()); - assertThat(previousTaskIds).isEqualTo(newTaskIds); - } - - @Test - void testDeleteWithCodeHints() { - var task = taskRepository.findByExerciseId(programmingExercise.getId()).stream().filter(task1 -> "Task 1".equals(task1.getTaskName())).findFirst().orElse(null); - assertThat(task).isNotNull(); - - var codeHint = new CodeHint(); - codeHint.setExercise(programmingExercise); - codeHint.setProgrammingExerciseTask(task); - codeHintRepository.save(codeHint); - - programmingExerciseTaskService.delete(task); - assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).hasSize(1); - assertThat(taskRepository.findById(task.getId())).isEmpty(); - assertThat(codeHintRepository.findByExerciseId(programmingExercise.getId())).isEmpty(); - } - - @Test - @WithMockUser(username = "instructor1", roles = "INSTRUCTOR") - void getTasksWithoutInactiveFiltersOutInactive() { - programmingExercise = programmingExerciseRepository - .findByIdWithEagerTestCasesStaticCodeAnalysisCategoriesHintsAndTemplateAndSolutionParticipationsAndAuxReposAndBuildConfig(programmingExercise.getId()) - .orElseThrow(); - testCaseRepository.deleteAll(programmingExercise.getTestCases()); - - String[] testCaseNames = { "testClass[BubbleSort]", "testParametrized(Parameter1, 2)[1]" }; - for (var name : testCaseNames) { - var testCase = new ProgrammingExerciseTestCase(); - testCase.setExercise(programmingExercise); - testCase.setTestName(name); - testCase.setActive(true); - testCaseRepository.save(testCase); - } - - var testCase = new ProgrammingExerciseTestCase(); - testCase.setExercise(programmingExercise); - testCase.setTestName("testWithBraces()"); - testCase.setActive(false); - testCaseRepository.save(testCase); - - programmingExercise = programmingExerciseRepository - .findByIdWithEagerTestCasesStaticCodeAnalysisCategoriesHintsAndTemplateAndSolutionParticipationsAndAuxReposAndBuildConfig(programmingExercise.getId()) - .orElseThrow(); - - updateProblemStatement(""" - [task][Task 1](testClass[BubbleSort],testWithBraces(),testParametrized(Parameter1, 2)[1]) - """); - - var actualTasks = programmingExerciseTaskService.getTasksWithoutInactiveTestCases(programmingExercise.getId()); - assertThat(actualTasks).hasSize(1); - - var actualTestCases = actualTasks.stream().findFirst().get().getTestCases(); - assertThat(actualTestCases).hasSize(2).allMatch(ProgrammingExerciseTestCase::isActive); - } - - @Test - @WithMockUser(username = "instructor1", roles = "INSTRUCTOR") - void testParseTestCaseNames() { - programmingExercise = programmingExerciseRepository - .findByIdWithEagerTestCasesStaticCodeAnalysisCategoriesHintsAndTemplateAndSolutionParticipationsAndAuxReposAndBuildConfig(programmingExercise.getId()) - .orElseThrow(); - testCaseRepository.deleteAll(programmingExercise.getTestCases()); - - String[] testCaseNames = new String[] { "testClass[BubbleSort]", "testWithBraces()", "testParametrized(Parameter1, 2)[1]" }; - for (var name : testCaseNames) { - var testCase = new ProgrammingExerciseTestCase(); - testCase.setExercise(programmingExercise); - testCase.setTestName(name); - testCase.setActive(true); - testCaseRepository.save(testCase); - } - programmingExercise = programmingExerciseRepository - .findByIdWithEagerTestCasesStaticCodeAnalysisCategoriesHintsAndTemplateAndSolutionParticipationsAndAuxReposAndBuildConfig(programmingExercise.getId()) - .orElseThrow(); - - updateProblemStatement(""" - [task][Task 1](testClass[BubbleSort],testWithBraces(),testParametrized(Parameter1, 2)[1]) - """); - - var actualTasks = taskRepository.findByExerciseId(programmingExercise.getId()); - assertThat(actualTasks).hasSize(1); - final var actualTask = actualTasks.iterator().next().getId(); - var actualTaskWithTestCases = taskRepository.findByIdWithTestCaseAndSolutionEntriesElseThrow(actualTask); - assertThat(actualTaskWithTestCases.getTaskName()).isEqualTo("Task 1"); - var actualTestCaseNames = actualTaskWithTestCases.getTestCases().stream().map(ProgrammingExerciseTestCase::getTestName).toList(); - assertThat(actualTestCaseNames).containsExactlyInAnyOrder(testCaseNames); - } - - @Test - @WithMockUser(username = "instructor1", roles = "INSTRUCTOR") - void testExtractTasksFromTestIds() { - var test1 = testCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testClass[BubbleSort]").orElseThrow(); - var test2 = testCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testMethods[Context]").orElseThrow(); - - updateProblemStatement("[task][Task 1](%s,%s)".formatted(test1.getId(), test2.getId())); - - var actualTasks = taskRepository.findByExerciseId(programmingExercise.getId()); - assertThat(actualTasks).hasSize(1); - final var actualTask = actualTasks.iterator().next().getId(); - var actualTaskWithTestCases = taskRepository.findByIdWithTestCaseAndSolutionEntriesElseThrow(actualTask); - assertThat(actualTaskWithTestCases.getTaskName()).isEqualTo("Task 1"); - var actualTestCaseNames = actualTaskWithTestCases.getTestCases().stream().map(ProgrammingExerciseTestCase::getTestName).toList(); - assertThat(actualTestCaseNames).containsExactlyInAnyOrder("testClass[BubbleSort]", "testMethods[Context]"); - } - - private boolean checkTaskEqual(ProgrammingExerciseTask task, String expectedName, String expectedTestName) { - var testCases = task.getTestCases(); - return expectedName.equals(task.getTaskName()) && !testCases.isEmpty() && expectedTestName.equals(testCases.stream().findFirst().get().getTestName()); - } - - @Test - void testNameReplacement() { - Map testCases = testCaseRepository.findByExerciseId(programmingExercise.getId()).stream() - .collect(Collectors.toMap(ProgrammingExerciseTestCase::getTestName, ProgrammingExerciseTestCase::getId)); - - programmingExerciseTaskService.replaceTestNamesWithIds(programmingExercise); - String problemStatement = programmingExercise.getProblemStatement(); - - // [task][Task 1](testClass[BubbleSort]) - // [task][Task 2](testMethods[Context]) - assertThat(problemStatement).as("no more test case names should be present").doesNotContain(testCases.keySet()) - .contains("[task][Task 1](%s)".formatted(testCases.get("testClass[BubbleSort]"))) - .contains("[task][Task 2](%s)".formatted(testCases.get("testMethods[Context]"))); - } - - @Test - void testNameReplacementKeepsInactiveTests() { - // Task 1 is inactive, task 2 does not exist - updateProblemStatement("[task][Task 1](testClass[BubbleSort])\n[task][Task 2](nonExistingTask)"); - var testCase = testCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testClass[BubbleSort]").orElseThrow(); - testCase.setActive(false); - testCaseRepository.save(testCase); - - programmingExerciseTaskService.replaceTestNamesWithIds(programmingExercise); - String problemStatement = programmingExercise.getProblemStatement(); - - // Task 1 still contains the name since the mentioned test case is not active - assertThat(problemStatement).contains("[task][Task 1](testClass[BubbleSort])").contains("[task][Task 2](nonExistingTask)"); - } - - @Test - void testNameReplacementSpecialNames() { - var bubbleSort = testCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testClass[BubbleSort]").orElseThrow(); - var braces = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "testWithBraces()"); - var parameterized = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "testParametrized(Parameter1, 2)[1]"); - updateProblemStatement(""" - [task][Task 1](testClass[BubbleSort],testWithBraces(),testParametrized(Parameter1, 2)[1]) - """); - - programmingExerciseTaskService.replaceTestNamesWithIds(programmingExercise); - String problemStatement = programmingExercise.getProblemStatement(); - - assertThat(problemStatement).as("no more test case names should be present") - .doesNotContain("testClass[BubbleSort]", "testWithBraces()", "testParametrized(Parameter1, 2)[1]") - .contains("[task][Task 1](%s,%s,%s)".formatted(bubbleSort.getId(), braces.getId(), parameterized.getId())); - } - - @Test - void replacePlantUMLTestsWithIds() { - var testConstructorsLinkedList = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "testConstructors[LinkedList]"); - var testMethodsLinkedList = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "testMethods[LinkedList]"); - var testAttributesListNode = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "testAttributes[ListNode]"); - var testConstructorsListNode = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "testConstructors[ListNode]"); - var testClassLinkedList = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "testClass[LinkedList]"); - var testAttributesLinkedList = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "testAttributes[LinkedList]"); - updateProblemStatement(""" - @startuml - - class LinkedList { - + LinkedList() - :+ toString(): String - } - - class ListNode { - - value: T - + ListNode() - + ListNode(T) - + ListNode(T, ListNode, ListNode) - } - - LinkedList -[hidden]> ListNode - - LinkedList --up-|> MyList #testsColor(testClass[LinkedList]) - LinkedList --> ListNode #testsColor(testAttributes[LinkedList]): first - LinkedList --> ListNode #testsColor(testAttributes[LinkedList]): last - ListNode -l-> ListNode #testsColor(testAttributes[ListNode]): previous - ListNode -r-> ListNode #testsColor(testAttributes[ListNode]): next - - hide empty fields - hide empty methods - hide circles - - @enduml"""); - - programmingExerciseTaskService.replaceTestNamesWithIds(programmingExercise); - String problemStatement = programmingExercise.getProblemStatement(); - - var allTests = List.of(testConstructorsLinkedList, testMethodsLinkedList, testAttributesListNode, testConstructorsListNode, testClassLinkedList, testAttributesLinkedList); - var allTestNames = allTests.stream().map(ProgrammingExerciseTestCase::getTestName).toList(); - var allExpectedReplacements = allTests.stream().map(DomainObject::getId).map(id -> "" + id + "").toList(); - - assertThat(problemStatement).as("All test names got replaced").doesNotContain(allTestNames).contains(allExpectedReplacements); - } - - @Test - void testNameReplacementOnlyWithinTasks() { - // Tests that the replacing of test names only addresses test names inside of tasks. - // If a test name gets used in the regular problem statement text it gets kept. - - var test1 = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "test"); - var test2 = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "taskTest"); - - updateProblemStatement("[task][Taskname](test, taskTest)\nThis description contains the words test and taskTest, which should not be replaced."); - - programmingExerciseTaskService.replaceTestNamesWithIds(programmingExercise); - String problemStatement = programmingExercise.getProblemStatement(); - - assertThat(problemStatement).contains("[task][Taskname](%s,%s)".formatted(test1.getId(), test2.getId())) - .contains("This description contains the words test and taskTest, which should not be replaced."); - } - - @Test - void testNameReplacementTaskNameSameAsTestName() { - var sort = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "sort"); - - updateProblemStatement(""" - [task][sort](sort) - Sort using the method sort. - @startuml - class LinkedList { - + sort() - } - @enduml"""); - - programmingExerciseTaskService.replaceTestNamesWithIds(programmingExercise); - String problemStatement = programmingExercise.getProblemStatement(); - - assertThat(problemStatement).contains("[task][sort](%s)".formatted(sort.getId())).contains("Sort using the method sort.") - .contains("%s)>+ sort()".formatted(sort.getId())); - } - - @Test - void testIdReplacementWithNames() { - var bubbleSort = testCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testClass[BubbleSort]").orElseThrow(); - var inactiveTest = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "testName"); - inactiveTest.setActive(false); - testCaseRepository.save(inactiveTest); - - updateProblemStatement("[task][Taskname](%s,%s)".formatted(bubbleSort.getId(), inactiveTest.getId())); - - programmingExerciseTaskService.replaceTestIdsWithNames(programmingExercise); - String problemStatement = programmingExercise.getProblemStatement(); - - // inactive tests should also get replaced - assertThat(problemStatement).doesNotContain("").contains("testClass[BubbleSort]", "testName"); - } - - @Test - void testUpdateIds() { - updateProblemStatement("[task][Taskname](1,2)"); - - programmingExerciseTaskService.updateTestIds(programmingExercise, Map.of(1L, 10L, 2L, 23L)); - String problemStatement = programmingExercise.getProblemStatement(); - - assertThat(problemStatement).contains("10", "23"); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/StructuralTestCaseServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/StructuralTestCaseServiceTest.java deleted file mode 100644 index 747032881267..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/StructuralTestCaseServiceTest.java +++ /dev/null @@ -1,554 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import java.io.IOException; -import java.time.ZonedDateTime; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.security.test.context.support.WithMockUser; - -import de.tum.cit.aet.artemis.assessment.domain.Visibility; -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTestCaseType; -import de.tum.cit.aet.artemis.programming.service.hestia.structural.StructuralSolutionEntryGenerationException; -import de.tum.cit.aet.artemis.programming.util.LocalRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; - -/** - * Tests for the StructuralTestCaseService - * Test if solution entries are generated as expected for structural tests - */ -class StructuralTestCaseServiceTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { - - private static final String TEST_PREFIX = "structuraltestcaseservice"; - - private final LocalRepository solutionRepo = new LocalRepository("main"); - - private final LocalRepository testRepo = new LocalRepository("main"); - - private ProgrammingExercise exercise; - - @Override - protected String getTestPrefix() { - return TEST_PREFIX; - } - - @BeforeEach - void initTestCase() { - Course course = courseUtilService.addEmptyCourse(); - userUtilService.addUsers(TEST_PREFIX, 0, 0, 0, 1); - exercise = ProgrammingExerciseFactory.generateProgrammingExercise(ZonedDateTime.now().minusDays(1), ZonedDateTime.now().plusDays(7), course); - } - - @AfterEach - void cleanup() throws IOException { - solutionRepo.resetLocalRepo(); - testRepo.resetLocalRepo(); - } - - private void addTestCaseToExercise(String name) { - var testCase = new ProgrammingExerciseTestCase(); - testCase.setTestName(name); - testCase.setExercise(exercise); - testCase.setVisibility(Visibility.ALWAYS); - testCase.setActive(true); - testCase.setWeight(1D); - testCase.setType(ProgrammingExerciseTestCaseType.STRUCTURAL); - testCaseRepository.save(testCase); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForSimpleClass() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \npublic class Test {}", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - } - }] - """, exercise, testRepo); - addTestCaseToExercise("testClass[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("package test;\n\npublic class Test {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForSimpleClassWithoutSource() throws Exception { - exercise = hestiaUtilTestService.setupSolution("empty", "", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - } - }] - """, exercise, testRepo); - addTestCaseToExercise("testClass[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("package test;\n\npublic class Test {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForClassWithAnnotations() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n @TestA1(123) @TestA2(test=\"Test String\") public class Test {}", exercise, - solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test", - "annotations": ["TestA1", "TestA2"] - } - }] - """, exercise, testRepo); - addTestCaseToExercise("testClass[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("package test;\n\n@TestA1(value=123)\n@TestA2(test=\"Test String\")\npublic class Test {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForComplexClass() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \nprivate abstract class Test extends Test2 implements TestI1, TestI2 {}", exercise, - solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["private", "abstract"], - "package": "test", - "superclass": "Test2", - "interfaces": ["TestI1", "TestI2"] - } - }] - """, exercise, testRepo); - addTestCaseToExercise("testClass[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("package test;\n\nprivate abstract class Test extends Test2 implements TestI1, TestI2 {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForComplexClassWithoutSource() throws Exception { - exercise = hestiaUtilTestService.setupSolution("empty", "", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["private", "abstract"], - "package": "test", - "superclass": "Test2", - "interfaces": ["TestI1", "TestI2"] - } - }] - """, exercise, testRepo); - addTestCaseToExercise("testClass[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("package test;\n\nprivate abstract class Test extends Test2 implements TestI1, TestI2 {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForGenericClass() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \npublic class Test> {}", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - } - }] - """, exercise, testRepo); - addTestCaseToExercise("testClass[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("package test;\n\npublic class Test> {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForInterface() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \npublic interface Test {}", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test", - "isInterface": true - } - }] - """, exercise, testRepo); - addTestCaseToExercise("testClass[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("package test;\n\npublic interface Test {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForEnum() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \npublic enum Test { CASE1, CASE2; }", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test", - "isEnum": true - }, - "enumValues": [ - "CASE1", - "CASE2" - ] - }] - """, exercise, testRepo); - addTestCaseToExercise("testClass[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("package test;\n\npublic enum Test {\n CASE1, CASE2\n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForSimpleAttribute() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \npublic class Test {private String attributeName;}", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - }, - "attributes" : [{ - "name": "attributeName", - "modifiers": ["private"], - "type": "String" - }] - }] - """, exercise, testRepo); - addTestCaseToExercise("testAttributes[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("private String attributeName;"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForAttributeWithAnnotations() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", - "package test;\n \npublic class Test {@TestA1(123) @TestA2(test=\"Test String\") private String attributeName;}", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - }, - "attributes" : [{ - "name": "attributeName", - "modifiers": ["private"], - "type": "String", - "annotations": ["TestA1", "TestA2"] - }] - }] - """, exercise, testRepo); - addTestCaseToExercise("testAttributes[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("@TestA1(value=123)\n@TestA2(test=\"Test String\")\nprivate String attributeName;"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForSimpleAttributeWithoutSource() throws Exception { - exercise = hestiaUtilTestService.setupSolution("empty", "", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - }, - "attributes" : [{ - "name": "attributeName", - "modifiers": ["private"], - "type": "String" - }] - }] - """, exercise, testRepo); - addTestCaseToExercise("testAttributes[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("private String attributeName;"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForComplexAttribute() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \npublic class Test {private static final List attributeName;}", exercise, - solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - }, - "attributes" : [{ - "name": "attributeName", - "modifiers": ["protected", "static", "final"], - "type": "List" - }] - }] - """, exercise, testRepo); - addTestCaseToExercise("testAttributes[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("protected static final List attributeName;"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForSimpleConstructor() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \npublic class Test {public Test() {}}", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - }, - "constructors": [{ - "modifiers": ["public"], - "parameters": [] - }] - }] - """, exercise, testRepo); - addTestCaseToExercise("testConstructors[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("public Test() {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForConstructorWithAnnotations() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \npublic class Test {@TestA1(123) @TestA2(test=\"Test String\") public Test() {}}", - exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - }, - "constructors": [{ - "modifiers": ["public"], - "parameters": [], - "annotations": ["TestA1", "TestA2"] - }] - }] - """, exercise, testRepo); - addTestCaseToExercise("testConstructors[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("@TestA1(value=123)\n@TestA2(test=\"Test String\")\npublic Test() {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForComplexConstructor() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \npublic class Test {protected Test(String s1, List dates) {}}", exercise, - solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - }, - "constructors": [{ - "modifiers": ["protected"], - "parameters": ["String","List"] - }] - }] - """, exercise, testRepo); - addTestCaseToExercise("testConstructors[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("protected Test(String s1, List dates) {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForSimpleMethod() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \npublic class Test {public void foo() {}}", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - }, - "methods": [{ - "name": "foo", - "modifiers": ["public"], - "parameters": [], - "returnType": "void" - }] - }] - """, exercise, testRepo); - addTestCaseToExercise("testMethods[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("public void foo() {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForMethodWithAnnotations() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", - "package test;\n \npublic class Test {@TestA1(123) @TestA2(test=\"Test String\") public void foo() {}}", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - }, - "methods": [{ - "name": "foo", - "modifiers": ["public"], - "parameters": [], - "returnType": "void", - "annotations": ["TestA1", "TestA2"] - }] - }] - """, exercise, testRepo); - addTestCaseToExercise("testMethods[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("@TestA1(value=123)\n@TestA2(test=\"Test String\")\npublic void foo() {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForComplexMethod() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", - "package test;\n \npublic class Test {protected static List foo(List list, String s) {}}", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - }, - "methods": [{ - "name": "foo", - "modifiers": ["protected", "static"], - "parameters": ["List", "String"], - "returnType": "List" - }] - }] - """, exercise, testRepo); - addTestCaseToExercise("testMethods[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("protected static List foo(List list, String s) {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForGenericMethod() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \npublic class Test {public > E foo(T[] arr) {}}", exercise, - solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test.json", """ - [{ - "class": { - "name": "Test", - "modifiers": ["public"], - "package": "test" - }, - "methods": [{ - "name": "foo", - "modifiers": ["public"], - "parameters": ["Object[]"], - "returnType": "List" - }] - }] - """, exercise, testRepo); - addTestCaseToExercise("testMethods[Test]"); - - var solutionEntries = structuralTestCaseService.generateStructuralSolutionEntries(exercise); - assertThat(solutionEntries).hasSize(1); - assertThat(solutionEntries.getFirst().getFilePath()).isEqualTo("src/test/Test.java"); - assertThat(solutionEntries.getFirst().getCode()).isEqualTo("public > E foo(T[] arr) {\n \n}"); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testForMissingTestJson() throws Exception { - exercise = hestiaUtilTestService.setupSolution("src/test/Test.java", "package test;\n \npublic class Test {}", exercise, solutionRepo); - exercise = hestiaUtilTestService.setupTests("src/test/TestTest.java", "package test;\n \npublic class TestTest {}", exercise, testRepo); - addTestCaseToExercise("testClass[Test]"); - - assertThatExceptionOfType(StructuralSolutionEntryGenerationException.class).isThrownBy(() -> structuralTestCaseService.generateStructuralSolutionEntries(exercise)); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageIntegrationTest.java deleted file mode 100644 index a9544af19d89..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageIntegrationTest.java +++ /dev/null @@ -1,114 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.ZoneId; -import java.time.ZonedDateTime; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import org.springframework.security.test.context.support.WithMockUser; - -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageFileReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; - -class TestwiseCoverageIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { - - private static final String TEST_PREFIX = "testwisecoverageint"; - - private ProgrammingExercise programmingExercise; - - private ProgrammingSubmission latestSolutionSubmission; - - private CoverageReport latestReport; - - @BeforeEach - void setup() { - userUtilService.addUsers(TEST_PREFIX, 1, 1, 0, 0); - final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, true, ProgrammingLanguage.JAVA); - programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - var solutionParticipation = solutionEntryRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseId(programmingExercise.getId()).orElseThrow(); - var unsavedPreviousSubmission = new ProgrammingSubmission(); - unsavedPreviousSubmission.setParticipation(solutionParticipation); - unsavedPreviousSubmission.setSubmissionDate(ZonedDateTime.of(2022, 4, 5, 12, 0, 0, 0, ZoneId.of("Europe/Berlin"))); - var previousSolutionSubmission = programmingSubmissionRepository.save(unsavedPreviousSubmission); - var unsavedLatestSubmission = new ProgrammingSubmission(); - unsavedLatestSubmission.setParticipation(solutionParticipation); - unsavedLatestSubmission.setSubmissionDate(ZonedDateTime.of(2022, 4, 5, 13, 0, 0, 0, ZoneId.of("Europe/Berlin"))); - latestSolutionSubmission = programmingSubmissionRepository.save(unsavedLatestSubmission); - - var testCase1 = testCaseRepository.save(new ProgrammingExerciseTestCase().exercise(programmingExercise).testName("test1()")); - var testCase2 = testCaseRepository.save(new ProgrammingExerciseTestCase().exercise(programmingExercise).testName("test2()")); - generateAndSaveSimpleReport(0.3, "src/de/tum/in/ase/BubbleSort.java", 15, 5, 1, 5, testCase1, previousSolutionSubmission); - latestReport = generateAndSaveSimpleReport(0.4, "src/de/tum/in/ase/BubbleSort.java", 20, 8, 1, 8, testCase2, latestSolutionSubmission); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void getLatestFullCoverageReportAsStudent() throws Exception { - request.get("/api/programming-exercises/" + programmingExercise.getId() + "/full-testwise-coverage-report", HttpStatus.FORBIDDEN, CoverageReport.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void getLatestFullCoverageReportAsTutor() throws Exception { - var fullReport = request.get("/api/programming-exercises/" + programmingExercise.getId() + "/full-testwise-coverage-report", HttpStatus.OK, CoverageReport.class); - assertThat(fullReport.getCoveredLineRatio()).isEqualTo(latestReport.getCoveredLineRatio()); - var fileReports = fullReport.getFileReports(); - assertThat(fileReports).hasSize(1); - var fileReport = fileReports.stream().toList().getFirst(); - assertThat(fileReport.getFilePath()).isEqualTo("src/de/tum/in/ase/BubbleSort.java"); - assertThat(fileReport.getCoveredLineCount()).isEqualTo(8); - assertThat(fileReport.getLineCount()).isEqualTo(20); - // the latest coverage report only contains one file report - assertThat(fileReport.getTestwiseCoverageEntries()) - .containsExactlyElementsOf(latestReport.getFileReports().stream().flatMap(report -> report.getTestwiseCoverageEntries().stream()).toList()); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void getLatestCoverageReportAsStudent() throws Exception { - request.get("/api/programming-exercises/" + programmingExercise.getId() + "/testwise-coverage-report", HttpStatus.FORBIDDEN, CoverageReport.class); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") - void getLatestCoverageReportAsTutor() throws Exception { - var report = request.get("/api/programming-exercises/" + programmingExercise.getId() + "/testwise-coverage-report", HttpStatus.OK, CoverageReport.class); - assertThat(report.getFileReports()).isNull(); - assertThat(report.getCoveredLineRatio()).isEqualTo(0.4); - } - - private CoverageReport generateAndSaveSimpleReport(double coveredLineRatio, String filePath, Integer fileLineCount, Integer coveredLineCount, Integer startLine, - Integer lineCount, ProgrammingExerciseTestCase testCase, ProgrammingSubmission submission) { - var unsavedLatestReport = new CoverageReport(); - unsavedLatestReport.setSubmission(latestSolutionSubmission); - unsavedLatestReport.setCoveredLineRatio(coveredLineRatio); - unsavedLatestReport.setSubmission(submission); - var resultReport = coverageReportRepository.save(unsavedLatestReport); - - var unsavedLatestFileReport = new CoverageFileReport(); - unsavedLatestFileReport.setFilePath(filePath); - unsavedLatestFileReport.setLineCount(fileLineCount); - unsavedLatestFileReport.setCoveredLineCount(coveredLineCount); - unsavedLatestFileReport.setFullReport(unsavedLatestReport); - coverageFileReportRepository.save(unsavedLatestFileReport); - - var unsavedLatestEntry = new TestwiseCoverageReportEntry(); - unsavedLatestEntry.setStartLine(startLine); - unsavedLatestEntry.setLineCount(lineCount); - unsavedLatestEntry.setTestCase(testCase); - unsavedLatestEntry.setFileReport(unsavedLatestFileReport); - testwiseCoverageReportEntryRepository.save(unsavedLatestEntry); - - return coverageReportRepository.findCoverageReportByIdWithEagerFileReportsAndEntriesElseThrow(resultReport.getId()); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageReportServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageReportServiceTest.java deleted file mode 100644 index 18addd11453e..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageReportServiceTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.util.Map; -import java.util.Set; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.data.domain.Pageable; -import org.springframework.security.test.context.support.WithMockUser; - -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; -import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; -import de.tum.cit.aet.artemis.programming.hestia.util.TestwiseCoverageTestUtil; -import de.tum.cit.aet.artemis.programming.util.LocalRepository; - -class TestwiseCoverageReportServiceTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { - - private static final String TEST_PREFIX = "testwisecoveragereportservice"; - - private ProgrammingExercise programmingExercise; - - private ProgrammingSubmission solutionSubmission; - - private final LocalRepository solutionRepo = new LocalRepository("main"); - - @Override - protected String getTestPrefix() { - return TEST_PREFIX; - } - - @BeforeEach - void setup() throws Exception { - userUtilService.addUsers(TEST_PREFIX, 1, 0, 0, 1); - final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, true, ProgrammingLanguage.JAVA); - programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - - programmingExercise = hestiaUtilTestService.setupSolution( - Map.ofEntries(Map.entry("src/de/tum/in/ase/BubbleSort.java", "\n ".repeat(28)), Map.entry("src/de/tum/in/ase/Context.java", "\n ".repeat(18))), programmingExercise, - solutionRepo); - - var testCase1 = new ProgrammingExerciseTestCase().testName("test1()").exercise(programmingExercise).active(true).weight(1.0); - testCaseRepository.save(testCase1); - var testCase2 = new ProgrammingExerciseTestCase().testName("test2()").exercise(programmingExercise).active(true).weight(1.0); - testCaseRepository.save(testCase2); - var solutionParticipation = solutionProgrammingExerciseRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseId(programmingExercise.getId()).orElseThrow(); - solutionSubmission = programmingExerciseUtilService.createProgrammingSubmission(solutionParticipation, false); - programmingExercise = programmingExerciseRepository.findByIdElseThrow(programmingExercise.getId()); - } - - @AfterEach - void cleanup() throws IOException { - solutionRepo.resetLocalRepo(); - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void shouldCreateFullTestwiseCoverageReport() { - var fileReportsByTestName = TestwiseCoverageTestUtil.generateCoverageFileReportByTestName(); - testwiseCoverageService.createTestwiseCoverageReport(fileReportsByTestName, programmingExercise, solutionSubmission); - - var reports = coverageReportRepository.getLatestCoverageReportsWithLegalSubmissionsForProgrammingExercise(programmingExercise.getId(), Pageable.ofSize(1)); - assertThat(reports).hasSize(1); - var report = reports.getFirst(); - // 18/50 lines covered = 32% - assertThat(report.getCoveredLineRatio()).isEqualTo(0.32); - - var testCases = testCaseRepository.findByExerciseId(programmingExercise.getId()); - var testCase1 = testCases.stream().filter(testCase -> "test1()".equals(testCase.getTestName())).findFirst().orElseThrow(); - var testCase2 = testCases.stream().filter(testCase -> "test2()".equals(testCase.getTestName())).findFirst().orElseThrow(); - - var optionalFullReportWithFileReports = coverageReportRepository.findCoverageReportByIdWithEagerFileReportsAndEntries(report.getId()); - assertThat(optionalFullReportWithFileReports).isPresent(); - var fullReportWithFileReports = optionalFullReportWithFileReports.get(); - var fileReports = fullReportWithFileReports.getFileReports(); - assertThat(fileReports).hasSize(2); - - var bubbleSortFileReport = fileReports.stream().filter(fileReport -> "src/de/tum/in/ase/BubbleSort.java".equals(fileReport.getFilePath())).findFirst().orElseThrow(); - var entriesBubbleSort = bubbleSortFileReport.getTestwiseCoverageEntries(); - assertThat(entriesBubbleSort).hasSize(4); - checkIfSetContainsEntry(entriesBubbleSort, 15, 3, testCase1); - checkIfSetContainsEntry(entriesBubbleSort, 23, 1, testCase1); - checkIfSetContainsEntry(entriesBubbleSort, 2, 1, testCase2); - checkIfSetContainsEntry(entriesBubbleSort, 16, 3, testCase2); - - var contextFileReport = fileReports.stream().filter(fileReport -> "src/de/tum/in/ase/Context.java".equals(fileReport.getFilePath())).findFirst().orElseThrow(); - assertThat(contextFileReport.getTestwiseCoverageEntries()).hasSize(1); - checkIfSetContainsEntry(contextFileReport.getTestwiseCoverageEntries(), 1, 10, testCase2); - } - - private void checkIfSetContainsEntry(Set set, Integer startLine, Integer lineCount, ProgrammingExerciseTestCase testCase) { - assertThat(set).anyMatch(entry -> startLine.equals(entry.getStartLine()) && lineCount.equals(entry.getLineCount()) && testCase.equals(entry.getTestCase())); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/AddUncoveredLinesAsPotentialCodeBlocksTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/AddUncoveredLinesAsPotentialCodeBlocksTest.java deleted file mode 100644 index 30ab4619e68e..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/AddUncoveredLinesAsPotentialCodeBlocksTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia.behavioral; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.AddUncoveredLinesAsPotentialCodeBlocks; - -class AddUncoveredLinesAsPotentialCodeBlocksTest { - - private AddUncoveredLinesAsPotentialCodeBlocks addPotentialCodeBlocks; - - private GroupedFile groupedFile; - - @BeforeEach - void initBlackboard() { - BehavioralBlackboard blackboard = new BehavioralBlackboard(null, null, null); - var groupedFiles = new ArrayList(); - blackboard.setGroupedFiles(groupedFiles); - groupedFile = new GroupedFile("test.java", null, null, null); - groupedFile.setCommonChanges(List.of(new GroupedFile.ChangeBlock(List.of(3, 4)))); - groupedFiles.add(groupedFile); - - addPotentialCodeBlocks = new AddUncoveredLinesAsPotentialCodeBlocks(blackboard); - } - - @Test - void testNoAction() { - assertThat(addPotentialCodeBlocks.executeCondition()).isFalse(); - } - - @Test - void testAddNoBlocks() { - groupedFile.setFileContent(""" - A - B - C - D - E - F - """); - - assertThat(addPotentialCodeBlocks.executeCondition()).isTrue(); - assertThat(addPotentialCodeBlocks.executeAction()).isFalse(); - assertThat(groupedFile.getCommonChanges()).isNotNull().containsExactly(new GroupedFile.ChangeBlock(List.of(3, 4))); - } - - @Test - void testAddPrefix() { - groupedFile.setFileContent(""" - - - C - D - E - F - """); - - assertThat(addPotentialCodeBlocks.executeCondition()).isTrue(); - assertThat(addPotentialCodeBlocks.executeAction()).isTrue(); - assertThat(groupedFile.getCommonChanges()).isNotNull().containsExactlyInAnyOrder(new GroupedFile.ChangeBlock(List.of(3, 4)), - new GroupedFile.ChangeBlock(List.of(1, 2), true)); - } - - @Test - void testAddPostfix() { - groupedFile.setFileContent(""" - A - B - C - D - - - X - """); - - assertThat(addPotentialCodeBlocks.executeCondition()).isTrue(); - assertThat(addPotentialCodeBlocks.executeAction()).isTrue(); - assertThat(groupedFile.getCommonChanges()).isNotNull().containsExactlyInAnyOrder(new GroupedFile.ChangeBlock(List.of(3, 4)), - new GroupedFile.ChangeBlock(List.of(5, 6), true)); - } - - @Test - void testIncludesCurlyBraces() { - groupedFile.setFileContent(""" - A - } - C - D - { - F - """); - - assertThat(addPotentialCodeBlocks.executeCondition()).isTrue(); - assertThat(addPotentialCodeBlocks.executeAction()).isTrue(); - assertThat(groupedFile.getCommonChanges()).isNotNull().containsExactlyInAnyOrder(new GroupedFile.ChangeBlock(List.of(3, 4)), new GroupedFile.ChangeBlock(List.of(2), true), - new GroupedFile.ChangeBlock(List.of(5), true)); - } - - @Test - void testIncludesElseStatements() { - groupedFile.setFileContent(""" - else - } else { - C - D - } else - else { - """); - - assertThat(addPotentialCodeBlocks.executeCondition()).isTrue(); - assertThat(addPotentialCodeBlocks.executeAction()).isTrue(); - assertThat(groupedFile.getCommonChanges()).isNotNull().containsExactlyInAnyOrder(new GroupedFile.ChangeBlock(List.of(3, 4)), - new GroupedFile.ChangeBlock(List.of(1, 2), true), new GroupedFile.ChangeBlock(List.of(5, 6), true)); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/BehavioralTestCaseServiceLocalCILocalVCTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/BehavioralTestCaseServiceLocalCILocalVCTest.java deleted file mode 100644 index d4de36c789e9..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/BehavioralTestCaseServiceLocalCILocalVCTest.java +++ /dev/null @@ -1,149 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia.behavioral; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.util.HashSet; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.security.test.context.support.WithMockUser; - -import de.tum.cit.aet.artemis.assessment.domain.Visibility; -import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageFileReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTestCaseType; -import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; -import de.tum.cit.aet.artemis.programming.util.LocalRepository; - -class BehavioralTestCaseServiceLocalCILocalVCTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { - - private static final String TEST_PREFIX = "behavioraltestcastservice"; - - private final LocalRepository solutionRepo = new LocalRepository("main"); - - private ProgrammingExercise exercise; - - @Override - protected String getTestPrefix() { - return TEST_PREFIX; - } - - @BeforeEach - void initTestCase() { - userUtilService.addUsers(TEST_PREFIX, 0, 0, 0, 1); - final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, true, ProgrammingLanguage.JAVA); - exercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - exercise.getBuildConfig().setTestwiseCoverageEnabled(true); - } - - @AfterEach - void cleanup() throws IOException { - solutionRepo.resetLocalRepo(); - } - - private ProgrammingExerciseTestCase addTestCaseToExercise(String name) { - var testCase = new ProgrammingExerciseTestCase(); - testCase.setTestName(name); - testCase.setExercise(exercise); - testCase.setVisibility(Visibility.ALWAYS); - testCase.setActive(true); - testCase.setWeight(1D); - testCase.setType(ProgrammingExerciseTestCaseType.BEHAVIORAL); - return testCaseRepository.save(testCase); - } - - private ProgrammingExerciseGitDiffReport newGitDiffReport() { - var gitDiffReport = new ProgrammingExerciseGitDiffReport(); - gitDiffReport.setEntries(new HashSet<>()); - gitDiffReport.setProgrammingExercise(exercise); - gitDiffReport.setSolutionRepositoryCommitHash("123a"); - gitDiffReport.setTemplateRepositoryCommitHash("123b"); - gitDiffReport = reportRepository.save(gitDiffReport); - return gitDiffReport; - } - - private ProgrammingExerciseGitDiffReport addGitDiffEntry(String filePath, int startLine, int lineCount, ProgrammingExerciseGitDiffReport gitDiffReport) { - var gitDiffEntry = new ProgrammingExerciseGitDiffEntry(); - gitDiffEntry.setFilePath(filePath); - gitDiffEntry.setStartLine(startLine); - gitDiffEntry.setLineCount(lineCount); - gitDiffEntry.setGitDiffReport(gitDiffReport); - gitDiffReport.getEntries().add(gitDiffEntry); - return reportRepository.save(gitDiffReport); - } - - private CoverageReport newCoverageReport() { - var solutionParticipation = solutionProgrammingExerciseRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseId(exercise.getId()).orElseThrow(); - var solutionSubmission = programmingExerciseUtilService.createProgrammingSubmission(solutionParticipation, false); - - var coverageReport = new CoverageReport(); - coverageReport.setFileReports(new HashSet<>()); - coverageReport.setSubmission(solutionSubmission); - coverageReport = coverageReportRepository.save(coverageReport); - return coverageReport; - } - - private CoverageFileReport newCoverageFileReport(String filePath, CoverageReport coverageReport) { - var coverageFileReport = new CoverageFileReport(); - coverageFileReport.setFilePath(filePath); - coverageFileReport.setTestwiseCoverageEntries(new HashSet<>()); - coverageFileReport.setFullReport(coverageReport); - coverageFileReport = coverageFileReportRepository.save(coverageFileReport); - coverageReport.getFileReports().add(coverageFileReport); - return coverageFileReport; - } - - private TestwiseCoverageReportEntry newCoverageReportEntry(int startLine, int lineCount, ProgrammingExerciseTestCase testCase, CoverageFileReport coverageFileReport) { - var coverageReportEntry = new TestwiseCoverageReportEntry(); - coverageReportEntry.setTestCase(testCase); - coverageReportEntry.setStartLine(startLine); - coverageReportEntry.setLineCount(lineCount); - coverageReportEntry.setFileReport(coverageFileReport); - coverageReportEntry = testwiseCoverageReportEntryRepository.save(coverageReportEntry); - coverageFileReport.getTestwiseCoverageEntries().add(coverageReportEntry); - return coverageReportEntry; - } - - @Test - @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGenerationForSimpleExample() throws Exception { - exercise = hestiaUtilTestService.setupSolution("Test.java", "A\nB\nC\nD\nE\nF\nG\nH", exercise, solutionRepo); - var testCase = addTestCaseToExercise("testCase"); - - var gitDiffReport = newGitDiffReport(); - addGitDiffEntry("Test.java", 2, 7, gitDiffReport); - - var coverageReport = newCoverageReport(); - var coverageFileReport = newCoverageFileReport("Test.java", coverageReport); - newCoverageReportEntry(1, 3, testCase, coverageFileReport); - newCoverageReportEntry(5, 2, testCase, coverageFileReport); - - var solutionEntries = behavioralTestCaseService.generateBehavioralSolutionEntries(exercise); - - var expected1 = new ProgrammingExerciseSolutionEntry(); - expected1.setId(0L); - expected1.setFilePath("Test.java"); - expected1.setTestCase(testCase); - expected1.setLine(2); - expected1.setCode("B\nC"); - var expected2 = new ProgrammingExerciseSolutionEntry(); - expected2.setId(0L); - expected2.setFilePath("Test.java"); - expected2.setTestCase(testCase); - expected2.setLine(5); - expected2.setCode("E\nF"); - assertThat(solutionEntries).isNotNull(); - solutionEntries.forEach(solutionEntry -> solutionEntry.setId(0L)); - assertThat(solutionEntries).containsExactlyInAnyOrder(expected1, expected2); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/CombineChangeBlocksTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/CombineChangeBlocksTest.java deleted file mode 100644 index a9c8e9689bd7..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/CombineChangeBlocksTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia.behavioral; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralSolutionEntryGenerationException; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile.ChangeBlock; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.CombineChangeBlocks; - -class CombineChangeBlocksTest { - - private CombineChangeBlocks combineChangeBlocks; - - private GroupedFile groupedFile; - - @BeforeEach - void initBlackboard() { - BehavioralBlackboard blackboard = new BehavioralBlackboard(null, null, null); - var groupedFiles = new ArrayList(); - blackboard.setGroupedFiles(groupedFiles); - groupedFile = new GroupedFile("test.java", null, null, null); - groupedFiles.add(groupedFile); - - combineChangeBlocks = new CombineChangeBlocks(blackboard); - } - - @Test - void testNoCombining() throws BehavioralSolutionEntryGenerationException { - var changeBlocks = new ChangeBlock[] { new ChangeBlock(List.of(1)), new ChangeBlock(List.of(3)), new ChangeBlock(List.of(5)) }; - groupedFile.setCommonChanges(List.of(changeBlocks)); - - assertThat(combineChangeBlocks.executeCondition()).isTrue(); - assertThat(combineChangeBlocks.executeAction()).isFalse(); - assertThat(groupedFile.getCommonChanges()).containsExactly(changeBlocks); - } - - @Test - void testOneCombination1() throws BehavioralSolutionEntryGenerationException { - var changeBlocks = new ChangeBlock[] { new ChangeBlock(List.of(2)), new ChangeBlock(List.of(3)) }; - groupedFile.setCommonChanges(List.of(changeBlocks)); - - assertThat(combineChangeBlocks.executeCondition()).isTrue(); - assertThat(combineChangeBlocks.executeAction()).isTrue(); - assertThat(groupedFile.getCommonChanges()).containsExactly(new ChangeBlock(List.of(2, 3))); - } - - @Test - void testOneCombination2() throws BehavioralSolutionEntryGenerationException { - var changeBlocks = new ChangeBlock[] { new ChangeBlock(List.of(1, 2, 3, 4)), new ChangeBlock(List.of(3, 4, 5, 6, 7, 8)) }; - groupedFile.setCommonChanges(List.of(changeBlocks)); - - assertThat(combineChangeBlocks.executeCondition()).isTrue(); - assertThat(combineChangeBlocks.executeAction()).isTrue(); - assertThat(groupedFile.getCommonChanges()).containsExactly(new ChangeBlock(List.of(1, 2, 3, 4, 5, 6, 7, 8))); - } - - @Test - void testCombineManyIntoOne1() throws BehavioralSolutionEntryGenerationException { - var changeBlocks = new ChangeBlock[] { new ChangeBlock(List.of(2)), new ChangeBlock(List.of(3)), new ChangeBlock(List.of(4)), new ChangeBlock(List.of(5)) }; - groupedFile.setCommonChanges(List.of(changeBlocks)); - - assertThat(combineChangeBlocks.executeCondition()).isTrue(); - assertThat(combineChangeBlocks.executeAction()).isTrue(); - assertThat(groupedFile.getCommonChanges()).containsExactly(new ChangeBlock(List.of(2, 3, 4, 5))); - } - - @Test - void testCombineManyIntoOne2() throws BehavioralSolutionEntryGenerationException { - var changeBlocks = new ChangeBlock[] { new ChangeBlock(List.of(1, 2, 3)), new ChangeBlock(List.of(3, 4, 5)), new ChangeBlock(List.of(4, 5, 6)), - new ChangeBlock(List.of(7, 8, 9)) }; - groupedFile.setCommonChanges(List.of(changeBlocks)); - - assertThat(combineChangeBlocks.executeCondition()).isTrue(); - assertThat(combineChangeBlocks.executeAction()).isTrue(); - assertThat(groupedFile.getCommonChanges()).containsExactly(new ChangeBlock(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9))); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/CreateCommonChangeBlocksTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/CreateCommonChangeBlocksTest.java deleted file mode 100644 index 40feb7c133be..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/CreateCommonChangeBlocksTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia.behavioral; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.CreateCommonChangeBlocks; - -class CreateCommonChangeBlocksTest { - - private CreateCommonChangeBlocks createCommonChangeBlocks; - - private GroupedFile groupedFile; - - @BeforeEach - void initBlackboard() { - BehavioralBlackboard blackboard = new BehavioralBlackboard(null, null, null); - var groupedFiles = new ArrayList(); - blackboard.setGroupedFiles(groupedFiles); - groupedFile = new GroupedFile("test.java", null, null, null); - groupedFiles.add(groupedFile); - - createCommonChangeBlocks = new CreateCommonChangeBlocks(blackboard); - } - - @Test - void testNoAction() { - groupedFile.setCommonLines(Collections.emptyList()); - groupedFile.setCommonChanges(Collections.emptyList()); - assertThat(createCommonChangeBlocks.executeCondition()).isFalse(); - } - - @Test - void testCreateOneChangeBlock() { - groupedFile.setCommonLines(Set.of(1, 2, 3)); - - assertThat(createCommonChangeBlocks.executeCondition()).isTrue(); - assertThat(createCommonChangeBlocks.executeAction()).isTrue(); - assertThat(groupedFile.getCommonChanges()).containsExactly(new GroupedFile.ChangeBlock(List.of(1, 2, 3))); - } - - @Test - void testCreateTwoChangeBlocks() { - groupedFile.setCommonLines(Set.of(1, 2, 4, 5)); - - assertThat(createCommonChangeBlocks.executeCondition()).isTrue(); - assertThat(createCommonChangeBlocks.executeAction()).isTrue(); - assertThat(groupedFile.getCommonChanges()).containsExactly(new GroupedFile.ChangeBlock(List.of(1, 2)), new GroupedFile.ChangeBlock(List.of(4, 5))); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/CreateSolutionEntriesTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/CreateSolutionEntriesTest.java deleted file mode 100644 index 20da41c8c3aa..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/CreateSolutionEntriesTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia.behavioral; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.CreateSolutionEntries; - -class CreateSolutionEntriesTest { - - private BehavioralBlackboard blackboard; - - private CreateSolutionEntries createSolutionEntries; - - private GroupedFile groupedFile; - - @BeforeEach - void initBlackboard() { - blackboard = new BehavioralBlackboard(null, null, null); - var groupedFiles = new ArrayList(); - blackboard.setGroupedFiles(groupedFiles); - groupedFile = new GroupedFile("test.java", new ProgrammingExerciseTestCase(), null, null); - groupedFiles.add(groupedFile); - - createSolutionEntries = new CreateSolutionEntries(blackboard); - } - - @Test - void testNoAction() { - assertThat(createSolutionEntries.executeCondition()).isFalse(); - } - - @Test - void testNoChangesOnSecondCall() { - groupedFile.setFileContent("A\nB\nC\nD"); - groupedFile.setCommonChanges(List.of(new GroupedFile.ChangeBlock(List.of(2, 3), false))); - - assertThat(createSolutionEntries.executeCondition()).isTrue(); - assertThat(createSolutionEntries.executeAction()).isTrue(); - var solutionEntries = blackboard.getSolutionEntries(); - assertThat(createSolutionEntries.executeAction()).isFalse(); - assertThat(blackboard.getSolutionEntries()).isSameAs(solutionEntries); - } - - @Test - void testCreateOneSolutionEntry() { - groupedFile.setFileContent("A\nB\nC\nD"); - groupedFile.setCommonChanges(List.of(new GroupedFile.ChangeBlock(List.of(2, 3), false))); - - assertThat(createSolutionEntries.executeCondition()).isTrue(); - assertThat(createSolutionEntries.executeAction()).isTrue(); - var expected = new ProgrammingExerciseSolutionEntry(); - expected.setId(0L); - expected.setFilePath("test.java"); - expected.setTestCase(groupedFile.getTestCase()); - expected.setLine(2); - expected.setCode("B\nC"); - assertThat(blackboard.getSolutionEntries()).isNotNull().containsExactly(expected); - } - - @Test - void testCreateOneSolutionEntryIgnoringPotential() { - groupedFile.setFileContent("A\nB\nC\nD\nE"); - groupedFile.setCommonChanges(List.of(new GroupedFile.ChangeBlock(List.of(2, 3), false), new GroupedFile.ChangeBlock(List.of(4, 5), true))); - - assertThat(createSolutionEntries.executeCondition()).isTrue(); - assertThat(createSolutionEntries.executeAction()).isTrue(); - var expected = new ProgrammingExerciseSolutionEntry(); - expected.setId(0L); - expected.setFilePath("test.java"); - expected.setTestCase(groupedFile.getTestCase()); - expected.setLine(2); - expected.setCode("B\nC"); - assertThat(blackboard.getSolutionEntries()).isNotNull().containsExactly(expected); - } - - @Test - void testCreateTwoSolutionEntries() { - groupedFile.setFileContent("A\nB\nC\nD\nE"); - groupedFile.setCommonChanges(List.of(new GroupedFile.ChangeBlock(List.of(2, 3), false), new GroupedFile.ChangeBlock(List.of(4, 5), false))); - - assertThat(createSolutionEntries.executeCondition()).isTrue(); - assertThat(createSolutionEntries.executeAction()).isTrue(); - var expected1 = new ProgrammingExerciseSolutionEntry(); - expected1.setId(0L); - expected1.setFilePath("test.java"); - expected1.setTestCase(groupedFile.getTestCase()); - expected1.setLine(2); - expected1.setCode("B\nC"); - var expected2 = new ProgrammingExerciseSolutionEntry(); - expected2.setId(0L); - expected2.setFilePath("test.java"); - expected2.setTestCase(groupedFile.getTestCase()); - expected2.setLine(4); - expected2.setCode("D\nE"); - assertThat(blackboard.getSolutionEntries()).isNotNull().containsExactlyInAnyOrder(expected1, expected2); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/DropRemovedGitDiffEntriesTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/DropRemovedGitDiffEntriesTest.java deleted file mode 100644 index 450203b02931..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/DropRemovedGitDiffEntriesTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia.behavioral; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.HashSet; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralSolutionEntryGenerationException; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.DropRemovedGitDiffEntries; - -class DropRemovedGitDiffEntriesTest { - - private BehavioralBlackboard blackboard; - - private ProgrammingExerciseGitDiffReport gitDiffReport; - - private DropRemovedGitDiffEntries dropRemovedGitDiffEntries; - - @BeforeEach - void setup() { - gitDiffReport = new ProgrammingExerciseGitDiffReport(); - gitDiffReport.setEntries(new HashSet<>()); - - blackboard = new BehavioralBlackboard(gitDiffReport, null, null); - dropRemovedGitDiffEntries = new DropRemovedGitDiffEntries(blackboard); - - } - - @Test - void testNoAction() { - assertThat(dropRemovedGitDiffEntries.executeCondition()).isFalse(); - } - - @Test - void testExecuteCondition() throws BehavioralSolutionEntryGenerationException { - var removedEntry = new ProgrammingExerciseGitDiffEntry(); - var addedEntry = new ProgrammingExerciseGitDiffEntry(); - addedEntry.setStartLine(1); - addedEntry.setLineCount(2); - gitDiffReport.setEntries(Set.of(removedEntry, addedEntry)); - - assertThat(dropRemovedGitDiffEntries.executeCondition()).isTrue(); - assertThat(dropRemovedGitDiffEntries.executeAction()).isTrue(); - var remainingEntries = blackboard.getGitDiffReport().getEntries(); - assertThat(dropRemovedGitDiffEntries.executeCondition()).isFalse(); - assertThat(remainingEntries).containsExactly(addedEntry); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/ExtractChangedLinesTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/ExtractChangedLinesTest.java deleted file mode 100644 index 016c4f5b6f21..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/ExtractChangedLinesTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia.behavioral; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.ExtractChangedLines; - -class ExtractChangedLinesTest { - - private ExtractChangedLines extractChangedLines; - - private GroupedFile groupedFile; - - private Set gitDiffEntries; - - @BeforeEach - void initBlackboard() { - BehavioralBlackboard blackboard = new BehavioralBlackboard(null, null, null); - var groupedFiles = new ArrayList(); - blackboard.setGroupedFiles(groupedFiles); - gitDiffEntries = new HashSet<>(); - groupedFile = new GroupedFile("test.java", null, gitDiffEntries, null); - groupedFiles.add(groupedFile); - - extractChangedLines = new ExtractChangedLines(blackboard); - } - - @Test - void testNoAction() { - groupedFile.setChangedLines(Collections.emptySet()); - assertThat(extractChangedLines.executeCondition()).isFalse(); - } - - @Test - void testExtractChangedLines1() { - var gitDiffEntry = new ProgrammingExerciseGitDiffEntry(); - gitDiffEntry.setStartLine(3); - gitDiffEntry.setLineCount(2); - gitDiffEntries.add(gitDiffEntry); - - assertThat(extractChangedLines.executeCondition()).isTrue(); - assertThat(extractChangedLines.executeAction()).isTrue(); - assertThat(groupedFile.getChangedLines()).containsExactly(3, 4); - } - - @Test - void testExtractChangedLines2() { - var gitDiffEntry1 = new ProgrammingExerciseGitDiffEntry(); - gitDiffEntry1.setStartLine(3); - gitDiffEntry1.setLineCount(2); - var gitDiffEntry2 = new ProgrammingExerciseGitDiffEntry(); - gitDiffEntry2.setStartLine(6); - gitDiffEntry2.setLineCount(3); - gitDiffEntries.add(gitDiffEntry1); - gitDiffEntries.add(gitDiffEntry2); - - assertThat(extractChangedLines.executeCondition()).isTrue(); - assertThat(extractChangedLines.executeAction()).isTrue(); - assertThat(groupedFile.getChangedLines()).containsExactly(3, 4, 6, 7, 8); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/ExtractCoveredLinesTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/ExtractCoveredLinesTest.java deleted file mode 100644 index 2be7206a11ae..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/ExtractCoveredLinesTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia.behavioral; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.ExtractCoveredLines; - -class ExtractCoveredLinesTest { - - private ExtractCoveredLines extractCoveredLines; - - private GroupedFile groupedFile; - - private Set coverageReportEntries; - - @BeforeEach - void initBlackboard() { - BehavioralBlackboard blackboard = new BehavioralBlackboard(null, null, null); - var groupedFiles = new ArrayList(); - blackboard.setGroupedFiles(groupedFiles); - coverageReportEntries = new HashSet<>(); - groupedFile = new GroupedFile("test.java", null, null, coverageReportEntries); - groupedFiles.add(groupedFile); - - extractCoveredLines = new ExtractCoveredLines(blackboard); - } - - @Test - void testNoAction() { - groupedFile.setCoveredLines(Collections.emptySet()); - assertThat(extractCoveredLines.executeCondition()).isFalse(); - } - - @Test - void testExtractChangedLines1() { - var reportEntry = new TestwiseCoverageReportEntry(); - reportEntry.setStartLine(3); - reportEntry.setLineCount(2); - coverageReportEntries.add(reportEntry); - - assertThat(extractCoveredLines.executeCondition()).isTrue(); - assertThat(extractCoveredLines.executeAction()).isTrue(); - assertThat(groupedFile.getCoveredLines()).containsExactly(3, 4); - } - - @Test - void testExtractChangedLines2() { - var reportEntry1 = new TestwiseCoverageReportEntry(); - reportEntry1.setStartLine(3); - reportEntry1.setLineCount(2); - var reportEntry2 = new TestwiseCoverageReportEntry(); - reportEntry2.setStartLine(6); - reportEntry2.setLineCount(3); - coverageReportEntries.add(reportEntry1); - coverageReportEntries.add(reportEntry2); - - assertThat(extractCoveredLines.executeCondition()).isTrue(); - assertThat(extractCoveredLines.executeAction()).isTrue(); - assertThat(groupedFile.getCoveredLines()).containsExactly(3, 4, 6, 7, 8); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/FindCommonLinesTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/FindCommonLinesTest.java deleted file mode 100644 index e36d44be008a..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/FindCommonLinesTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia.behavioral; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.ArrayList; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.FindCommonLines; - -class FindCommonLinesTest { - - private FindCommonLines findCommonLines; - - private GroupedFile groupedFile; - - @BeforeEach - void initBlackboard() { - BehavioralBlackboard blackboard = new BehavioralBlackboard(null, null, null); - var groupedFiles = new ArrayList(); - blackboard.setGroupedFiles(groupedFiles); - groupedFile = new GroupedFile("test.java", null, null, null); - groupedFiles.add(groupedFile); - - findCommonLines = new FindCommonLines(blackboard); - } - - @Test - void testNoAction() { - assertThat(findCommonLines.executeCondition()).isFalse(); - } - - @Test - void testFindCommonLines() { - groupedFile.setCoveredLines(Set.of(1, 2, 3)); - groupedFile.setChangedLines(Set.of(2, 3, 4)); - - assertThat(findCommonLines.executeCondition()).isTrue(); - assertThat(findCommonLines.executeAction()).isTrue(); - assertThat(groupedFile.getCommonLines()).containsExactly(2, 3); - } - - @Test - void testFindNoCommonLines() { - groupedFile.setCoveredLines(Set.of(1, 2, 3)); - groupedFile.setChangedLines(Set.of(4, 5, 6)); - - assertThat(findCommonLines.executeCondition()).isTrue(); - assertThat(findCommonLines.executeAction()).isFalse(); - assertThat(groupedFile.getCommonLines()).isEmpty(); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/GroupGitDiffAndCoverageEntriesByFilePathAndTestCaseTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/GroupGitDiffAndCoverageEntriesByFilePathAndTestCaseTest.java deleted file mode 100644 index 1e624e5b130b..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/GroupGitDiffAndCoverageEntriesByFilePathAndTestCaseTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia.behavioral; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageFileReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.GroupGitDiffAndCoverageEntriesByFilePathAndTestCase; - -class GroupGitDiffAndCoverageEntriesByFilePathAndTestCaseTest { - - private BehavioralBlackboard blackboard; - - private GroupGitDiffAndCoverageEntriesByFilePathAndTestCase grouper; - - @BeforeEach - void initBlackboard() { - blackboard = new BehavioralBlackboard(new ProgrammingExerciseGitDiffReport(), new CoverageReport(), null); - blackboard.getGitDiffReport().setEntries(new HashSet<>()); - blackboard.getCoverageReport().setFileReports(new HashSet<>()); - - grouper = new GroupGitDiffAndCoverageEntriesByFilePathAndTestCase(blackboard); - } - - @Test - void testNoAction() { - blackboard.setGroupedFiles(Collections.emptyList()); - assertThat(grouper.executeCondition()).isFalse(); - } - - @Test - void testNoFiles() { - assertThat(grouper.executeCondition()).isTrue(); - assertThat(grouper.executeAction()).isFalse(); - } - - @Test - void testGroupSingleFileAndTest() { - var testCase = new ProgrammingExerciseTestCase(); - var gitDiffEntry1 = addGitDiffEntry("test.java", 1, 10); - var gitDiffEntry2 = addGitDiffEntry("test.java", 12, 6); - var coverageEntry1 = addCoverageEntry("test.java", 5, 5, testCase); - var coverageEntry2 = addCoverageEntry("test.java", 15, 2, testCase); - - assertThat(grouper.executeCondition()).isTrue(); - assertThat(grouper.executeAction()).isTrue(); - assertThat(blackboard.getGroupedFiles()).isNotNull().hasSize(1) - .containsExactly(new GroupedFile("test.java", testCase, Set.of(gitDiffEntry1, gitDiffEntry2), Set.of(coverageEntry1, coverageEntry2))); - } - - @Test - void testGroupTwoFilesSingleTest() { - var testCase = new ProgrammingExerciseTestCase(); - var gitDiffEntry1 = addGitDiffEntry("test.java", 1, 20); - var gitDiffEntry2 = addGitDiffEntry("test2.java", 1, 20); - var coverageEntry1 = addCoverageEntry("test.java", 5, 5, testCase); - var coverageEntry2 = addCoverageEntry("test2.java", 15, 2, testCase); - - assertThat(grouper.executeCondition()).isTrue(); - assertThat(grouper.executeAction()).isTrue(); - assertThat(blackboard.getGroupedFiles()).isNotNull().hasSize(2).containsExactlyInAnyOrder( - new GroupedFile("test.java", testCase, Set.of(gitDiffEntry1), Set.of(coverageEntry1)), - new GroupedFile("test2.java", testCase, Set.of(gitDiffEntry2), Set.of(coverageEntry2))); - } - - @Test - void testGroupSingleFileTwoTests() { - var testCase1 = new ProgrammingExerciseTestCase(); - var testCase2 = new ProgrammingExerciseTestCase(); - var gitDiffEntry = addGitDiffEntry("test.java", 1, 20); - var coverageEntry1 = addCoverageEntry("test.java", 5, 5, testCase1); - var coverageEntry2 = addCoverageEntry("test.java", 15, 2, testCase2); - - assertThat(grouper.executeCondition()).isTrue(); - assertThat(grouper.executeAction()).isTrue(); - assertThat(blackboard.getGroupedFiles()).isNotNull().hasSize(2).containsExactlyInAnyOrder( - new GroupedFile("test.java", testCase1, Set.of(gitDiffEntry), Set.of(coverageEntry1)), - new GroupedFile("test.java", testCase2, Set.of(gitDiffEntry), Set.of(coverageEntry2))); - } - - private ProgrammingExerciseGitDiffEntry addGitDiffEntry(String filePath, int startLine, int lineCount) { - var gitDiffEntry = new ProgrammingExerciseGitDiffEntry(); - gitDiffEntry.setFilePath(filePath); - gitDiffEntry.setStartLine(startLine); - gitDiffEntry.setStartLine(lineCount); - blackboard.getGitDiffReport().getEntries().add(gitDiffEntry); - return gitDiffEntry; - } - - private TestwiseCoverageReportEntry addCoverageEntry(String filePath, int startLine, int lineCount, ProgrammingExerciseTestCase testCase) { - var coverageFileReport = blackboard.getCoverageReport().getFileReports().stream().filter(fileReport -> filePath.equals(fileReport.getFilePath())).findFirst() - .orElse(new CoverageFileReport()); - coverageFileReport.setFilePath(filePath); - if (coverageFileReport.getTestwiseCoverageEntries() == null) { - coverageFileReport.setTestwiseCoverageEntries(new HashSet<>()); - } - var coverageReportEntry = new TestwiseCoverageReportEntry(); - coverageReportEntry.setStartLine(startLine); - coverageReportEntry.setLineCount(lineCount); - coverageReportEntry.setTestCase(testCase); - coverageFileReport.getTestwiseCoverageEntries().add(coverageReportEntry); - blackboard.getCoverageReport().getFileReports().add(coverageFileReport); - return coverageReportEntry; - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/InsertFileContentsTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/InsertFileContentsTest.java deleted file mode 100644 index e0ed10e61fa2..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/InsertFileContentsTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia.behavioral; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralSolutionEntryGenerationException; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.GroupedFile; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.knowledgesource.InsertFileContents; - -class InsertFileContentsTest { - - private InsertFileContents insertFileContents; - - private GroupedFile groupedFile; - - private Map solutionRepoFiles; - - @BeforeEach - void initBlackboard() { - solutionRepoFiles = new HashMap<>(); - BehavioralBlackboard blackboard = new BehavioralBlackboard(null, null, solutionRepoFiles); - var groupedFiles = new ArrayList(); - blackboard.setGroupedFiles(groupedFiles); - groupedFile = new GroupedFile("test.java", null, null, null); - groupedFiles.add(groupedFile); - - insertFileContents = new InsertFileContents(blackboard); - } - - @Test - void testNoAction() { - solutionRepoFiles.put("test.java", "Something else"); - groupedFile.setFileContent("Something"); - - assertThat(insertFileContents.executeCondition()).isFalse(); - } - - @Test - void testAddContent() throws BehavioralSolutionEntryGenerationException { - solutionRepoFiles.put("test.java", "Something"); - - assertThat(insertFileContents.executeCondition()).isTrue(); - assertThat(insertFileContents.executeAction()).isTrue(); - assertThat(groupedFile.getFileContent()).isEqualTo("Something"); - } - - @Test - void testInvalidContent() { - assertThat(insertFileContents.executeCondition()).isTrue(); - assertThatExceptionOfType(BehavioralSolutionEntryGenerationException.class).isThrownBy(() -> insertFileContents.executeAction()); - } -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/util/TestwiseCoverageTestUtil.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/util/TestwiseCoverageTestUtil.java deleted file mode 100644 index 4dae98fee0b5..000000000000 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/util/TestwiseCoverageTestUtil.java +++ /dev/null @@ -1,49 +0,0 @@ -package de.tum.cit.aet.artemis.programming.hestia.util; - -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageFileReport; -import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; - -public class TestwiseCoverageTestUtil { - - public static Map> generateCoverageFileReportByTestName() { - var fileReport1_1 = new CoverageFileReport(); - fileReport1_1.setFilePath("src/de/tum/in/ase/BubbleSort.java"); - var lineCountByStartLine1_1 = Map.ofEntries(Map.entry(15, 3), Map.entry(23, 1)); - var entries1_1 = lineCountByStartLine1_1.entrySet().stream().map((mapEntry) -> { - var entry = new TestwiseCoverageReportEntry(); - entry.setStartLine(mapEntry.getKey()); - entry.setLineCount(mapEntry.getValue()); - return entry; - }).collect(Collectors.toSet()); - fileReport1_1.setTestwiseCoverageEntries(entries1_1); - - var fileReport2_1 = new CoverageFileReport(); - fileReport2_1.setFilePath("src/de/tum/in/ase/BubbleSort.java"); - var lineCountByStartLine2_1 = Map.ofEntries(Map.entry(2, 1), Map.entry(16, 3)); - var entries2_1 = lineCountByStartLine2_1.entrySet().stream().map((mapEntry) -> { - var entry = new TestwiseCoverageReportEntry(); - entry.setStartLine(mapEntry.getKey()); - entry.setLineCount(mapEntry.getValue()); - return entry; - }).collect(Collectors.toSet()); - fileReport2_1.setTestwiseCoverageEntries(entries2_1); - - var fileReport2_2 = new CoverageFileReport(); - fileReport2_2.setFilePath("src/de/tum/in/ase/Context.java"); - var lineCountByStartLine2_2 = Map.ofEntries(Map.entry(1, 10)); - var entries2_2 = lineCountByStartLine2_2.entrySet().stream().map((mapEntry) -> { - var entry = new TestwiseCoverageReportEntry(); - entry.setStartLine(mapEntry.getKey()); - entry.setLineCount(mapEntry.getValue()); - return entry; - }).collect(Collectors.toSet()); - fileReport2_2.setTestwiseCoverageEntries(entries2_2); - - return Map.ofEntries(Map.entry("test1()", Set.of(fileReport1_1)), Map.entry("test2()", Set.of(fileReport2_1, fileReport2_2))); - } - -} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIIntegrationTest.java index e3979dc830fb..52ccfd718e22 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIIntegrationTest.java @@ -173,7 +173,7 @@ void testBuildJobPersistence() { assertThat(buildJob.getCourseId()).isEqualTo(course.getId()); assertThat(buildJob.getExerciseId()).isEqualTo(programmingExercise.getId()); assertThat(buildJob.getParticipationId()).isEqualTo(studentParticipation.getId()); - assertThat(buildJob.getDockerImage()).isEqualTo(programmingExercise.getBuildConfig().getWindfile().getMetadata().docker().getFullImageName()); + assertThat(buildJob.getDockerImage()).isEqualTo(programmingExercise.getBuildConfig().getWindfile().metadata().docker().getFullImageName()); assertThat(buildJob.getRepositoryName()).isEqualTo(assignmentRepositorySlug); assertThat(buildJob.getBuildAgentAddress()).isNotEmpty(); assertThat(buildJob.getPriority()).isEqualTo(2); @@ -567,7 +567,7 @@ void testGetSubmissionReturnsWhenSubmissionProcessing() throws Exception { submission = programmingExerciseUtilService.addProgrammingSubmission(programmingExercise, submission, TEST_PREFIX + "student1"); JobTimingInfo jobTimingInfo = new JobTimingInfo(ZonedDateTime.now().minusSeconds(30), ZonedDateTime.now(), null, ZonedDateTime.now().plusSeconds(30), 60); - BuildConfig buildConfig = new BuildConfig(null, null, commitHash, commitHash, null, null, null, null, false, false, false, null, 0, null, null, null, null); + BuildConfig buildConfig = new BuildConfig(null, null, commitHash, commitHash, null, null, null, null, false, false, null, 0, null, null, null, null); BuildJobQueueItem buildJobQueueItem = new BuildJobQueueItem("1", "1", null, submission.getParticipation().getId(), 1L, programmingExercise.getId(), 0, 1, null, null, jobTimingInfo, buildConfig, null); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResourceIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResourceIntegrationTest.java index 8ab4e99eb38e..4a5219c6ba7b 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResourceIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResourceIntegrationTest.java @@ -88,7 +88,7 @@ void createJobs() { JobTimingInfo jobTimingInfo2 = new JobTimingInfo(ZonedDateTime.now(), ZonedDateTime.now().plusMinutes(1), ZonedDateTime.now().plusMinutes(2), null, 20); JobTimingInfo jobTimingInfo3 = new JobTimingInfo(ZonedDateTime.now().minusMinutes(10), ZonedDateTime.now().minusMinutes(9), ZonedDateTime.now().plusSeconds(150), null, 20); - BuildConfig buildConfig = new BuildConfig("echo 'test'", "test", "test", "test", "test", "test", null, null, false, false, false, null, 0, null, null, null, null); + BuildConfig buildConfig = new BuildConfig("echo 'test'", "test", "test", "test", "test", "test", null, null, false, false, null, 0, null, null, null, null); RepositoryInfo repositoryInfo = new RepositoryInfo("test", null, RepositoryType.USER, "test", "test", "test", null, null); String memberAddress = hazelcastInstance.getCluster().getLocalMember().getAddress().toString(); @@ -282,7 +282,7 @@ void testGetFinishedBuildJobs_returnsFilteredJobs() throws Exception { // Create a failed job to filter for JobTimingInfo jobTimingInfo = new JobTimingInfo(ZonedDateTime.now().plusDays(1), ZonedDateTime.now().plusDays(1).plusMinutes(2), ZonedDateTime.now().plusDays(1).plusMinutes(10), null, 0); - BuildConfig buildConfig = new BuildConfig("echo 'test'", "test", "test", "test", "test", "test", null, null, false, false, false, null, 0, null, null, null, null); + BuildConfig buildConfig = new BuildConfig("echo 'test'", "test", "test", "test", "test", "test", null, null, false, false, null, 0, null, null, null, null); RepositoryInfo repositoryInfo = new RepositoryInfo("test", null, RepositoryType.USER, "test", "test", "test", null, null); var failedJob1 = new BuildJobQueueItem("5", "job5", buildAgent, 1, course.getId(), 1, 1, 1, BuildStatus.FAILED, repositoryInfo, jobTimingInfo, buildConfig, null); var jobResult = new Result().successful(false).rated(true).score(0D).assessmentType(AssessmentType.AUTOMATIC).completionDate(ZonedDateTime.now()); @@ -402,7 +402,7 @@ void testBuildJob() throws Exception { JobTimingInfo jobTimingInfo4 = new JobTimingInfo(now.plusSeconds(2), null, null, null, 24); JobTimingInfo jobTimingInfo5 = new JobTimingInfo(now.plusSeconds(3), null, null, null, 24); - BuildConfig buildConfig = new BuildConfig("echo 'test'", "test", "test", "test", "test", "test", null, null, false, false, false, null, 0, null, null, null, null); + BuildConfig buildConfig = new BuildConfig("echo 'test'", "test", "test", "test", "test", "test", null, null, false, false, null, 0, null, null, null, null); RepositoryInfo repositoryInfo = new RepositoryInfo("test", null, RepositoryType.USER, "test", "test", "test", null, null); var job1 = new BuildJobQueueItem("1", "job1", buildAgent, 1, course.getId(), 1, 1, 1, BuildStatus.SUCCESSFUL, repositoryInfo, jobTimingInfo1, buildConfig, null); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIServiceTest.java index ef4e012901da..7ea4ceafddbf 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIServiceTest.java @@ -33,7 +33,7 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.RepositoryType; import de.tum.cit.aet.artemis.programming.dto.CheckoutDirectoriesDTO; -import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile; +import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService.BuildStatus; class LocalCIServiceTest extends AbstractProgrammingIntegrationLocalCILocalVCTest { @@ -71,7 +71,7 @@ void testReturnCorrectBuildStatus() { ProgrammingExerciseStudentParticipation participation = participationUtilService.addStudentParticipationForProgrammingExercise(exercise, TEST_PREFIX + "student1"); JobTimingInfo jobTimingInfo = new JobTimingInfo(ZonedDateTime.now(), ZonedDateTime.now().plusMinutes(1), ZonedDateTime.now().plusMinutes(2), null, 0); - BuildConfig buildConfig = new BuildConfig("echo 'test'", "test", "test", "test", "test", "test", null, null, false, false, false, null, 0, null, null, null, null); + BuildConfig buildConfig = new BuildConfig("echo 'test'", "test", "test", "test", "test", "test", null, null, false, false, null, 0, null, null, null, null); RepositoryInfo repositoryInfo = new RepositoryInfo("test", null, RepositoryType.USER, "test", "test", "test", null, null); String memberAddress = hazelcastInstance.getCluster().getLocalMember().getAddress().toString(); @@ -114,7 +114,7 @@ void testRecreateBuildPlanForExercise() throws IOException { exercise.getBuildConfig().setBuildPlanConfiguration(null); continuousIntegrationService.recreateBuildPlansForExercise(exercise); script = buildScriptProviderService.getScriptFor(exercise.getProgrammingLanguage(), Optional.ofNullable(exercise.getProjectType()), exercise.isStaticCodeAnalysisEnabled(), - exercise.getBuildConfig().hasSequentialTestRuns(), exercise.getBuildConfig().isTestwiseCoverageEnabled()); + exercise.getBuildConfig().hasSequentialTestRuns()); Windfile windfile = aeolusTemplateService.getDefaultWindfileFor(exercise); String actualBuildConfig = exercise.getBuildConfig().getBuildPlanConfiguration(); String expectedBuildConfig = new ObjectMapper().writeValueAsString(windfile); @@ -133,7 +133,6 @@ void testGetScriptForWithoutCache() { programmingExercise.setProjectType(null); programmingExercise.setStaticCodeAnalysisEnabled(false); programmingExercise.getBuildConfig().setSequentialTestRuns(false); - programmingExercise.getBuildConfig().setTestwiseCoverageEnabled(false); String script = buildScriptProviderService.getScriptFor(programmingExercise); assertThat(script).isNotNull(); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCITestService.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCITestService.java index 7ad94838921f..cb5d100e2d9f 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCITestService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCITestService.java @@ -181,7 +181,7 @@ public void mockInspectImage(DockerClient dockerClient) { * @param dockerClient the DockerClient to mock. * @param resourceRegexPattern the regex pattern that the resource path must match. The resource path is the path of the file or directory inside the container. * @param dataToReturn the data to return inside the InputStream in form of a map. Each entry of the map will be one TarArchiveEntry with the key denoting the - * tarArchiveEntry.getName() and the value being the content of the TarArchiveEntry. There can be up to two dataToReturn entries, in which case + * tarArchiveEntry.name() and the value being the content of the TarArchiveEntry. There can be up to two dataToReturn entries, in which case * the first call to "copyArchiveFromContainerCmd().exec()" will return the first entry, and the second call will return the second entry. * @throws IOException if the InputStream cannot be created. */ @@ -213,7 +213,7 @@ public final void mockInputStreamReturnedFromContainer(DockerClient dockerClient } /** - * Create a BufferedInputStream from a map. Each entry of the map will be one TarArchiveEntry with the key denoting the tarArchiveEntry.getName() and the value being the + * Create a BufferedInputStream from a map. Each entry of the map will be one TarArchiveEntry with the key denoting the tarArchiveEntry.name() and the value being the * content. * The returned InputStream can be used to mock the InputStream returned by dockerClient.copyArchiveFromContainerCmd(String containerId, String resource).exec(). * diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/ConsistencyCheckTestService.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/ConsistencyCheckTestService.java index 376fdaaf761e..7484326756bd 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/ConsistencyCheckTestService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/ConsistencyCheckTestService.java @@ -95,7 +95,7 @@ public void testCheckConsistencyOfProgrammingExercise_missingVCSProject() throws var consistencyErrors = request.getList("/api/programming-exercises/" + exercise.getId() + "/consistency-check", HttpStatus.OK, ConsistencyErrorDTO.class); assertThat(consistencyErrors).hasSize(1); - assertThat(consistencyErrors.getFirst().getType()).isEqualTo(ConsistencyErrorDTO.ErrorType.VCS_PROJECT_MISSING); + assertThat(consistencyErrors.getFirst().type()).isEqualTo(ConsistencyErrorDTO.ErrorType.VCS_PROJECT_MISSING); } /** diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseFeedbackCreationServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseFeedbackCreationServiceTest.java index 2140b3586c7b..27591fdb993a 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseFeedbackCreationServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseFeedbackCreationServiceTest.java @@ -19,11 +19,11 @@ import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCaseType; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.StaticCodeAnalysisTool; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTestCaseType; -import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO; +import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification; import de.tum.cit.aet.artemis.programming.dto.StaticCodeAnalysisIssue; import de.tum.cit.aet.artemis.programming.dto.StaticCodeAnalysisReportDTO; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; @@ -219,7 +219,7 @@ void createFeedbackFromTestCaseSuccessfulNoMessage() { assertThat(createFeedbackFromTestCase("test1", List.of(), true)).isNull(); } - private AbstractBuildResultNotificationDTO generateResult(List successfulTests, List failedTests) { + private BuildResultNotification generateResult(List successfulTests, List failedTests) { return ProgrammingExerciseFactory.generateTestResultDTO(null, "SOLUTION", null, programmingExercise.getProgrammingLanguage(), false, successfulTests, failedTests, null, null, null); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/connectors/jenkins/build_plan/JenkinsPipelineScriptCreatorTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/connectors/jenkins/build_plan/JenkinsPipelineScriptCreatorTest.java index 34afe9865fd2..a6bfdccc28de 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/connectors/jenkins/build_plan/JenkinsPipelineScriptCreatorTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/connectors/jenkins/build_plan/JenkinsPipelineScriptCreatorTest.java @@ -28,7 +28,6 @@ void init() { programmingExercise.setProjectType(ProjectType.MAVEN_MAVEN); programmingExercise.setStaticCodeAnalysisEnabled(true); programmingExercise.getBuildConfig().setSequentialTestRuns(false); - programmingExercise.getBuildConfig().setTestwiseCoverageEnabled(false); programmingExercise.setReleaseDate(null); course.addExercises(programmingExercise); @@ -48,9 +47,7 @@ void testBuildPlanCreation() { void testReplacements() { jenkinsPipelineScriptCreator.createBuildPlanForExercise(programmingExercise); BuildPlan buildPlan = buildPlanRepository.findByProgrammingExercises_IdWithProgrammingExercises(programmingExercise.getId()).orElseThrow(); - assertThat(buildPlan.getBuildPlan()).doesNotContain("#isStaticCodeAnalysisEnabled", "#testWiseCoverage", "#dockerImage", "#dockerArgs") - // testwise coverage is disabled in the dummy exercise - .contains("isTestwiseCoverageEnabled = false && isSolutionBuild"); + assertThat(buildPlan.getBuildPlan()).doesNotContain("#isStaticCodeAnalysisEnabled", "#dockerImage", "#dockerArgs").contains("isSolutionBuild"); } @Test diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/ProgrammingExerciseTaskTestRepository.java b/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/ProgrammingExerciseTaskTestRepository.java index 7d79da2bdcb6..8da13dec7aa4 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/ProgrammingExerciseTaskTestRepository.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/ProgrammingExerciseTaskTestRepository.java @@ -1,18 +1,12 @@ package de.tum.cit.aet.artemis.programming.test_repository; -import java.util.Optional; import java.util.Set; -import jakarta.validation.constraints.NotNull; - import org.springframework.context.annotation.Primary; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTask; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTaskRepository; /** * Spring Data repository for the ProgrammingExerciseTask entity. @@ -22,31 +16,4 @@ public interface ProgrammingExerciseTaskTestRepository extends ProgrammingExerciseTaskRepository { Set findByExerciseId(Long exerciseId); - - /** - * Gets a task with its programming exercise, test cases and solution entries of the test cases - * - * @param entryId The id of the task - * @return The task with the given ID if found - * @throws EntityNotFoundException If no task with the given ID was found - */ - @NotNull - default ProgrammingExerciseTask findByIdWithTestCaseAndSolutionEntriesElseThrow(long entryId) throws EntityNotFoundException { - return getValueElseThrow(findByIdWithTestCaseAndSolutionEntries(entryId), entryId); - } - - /** - * Gets a task with its programming exercise, test cases and solution entries of the test cases - * - * @param entryId The id of the task - * @return The task with the given ID - */ - @Query(""" - SELECT t - FROM ProgrammingExerciseTask t - LEFT JOIN FETCH t.testCases tc - LEFT JOIN FETCH tc.solutionEntries - WHERE t.id = :entryId - """) - Optional findByIdWithTestCaseAndSolutionEntries(@Param("entryId") long entryId); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/ProgrammingExerciseTestCaseTestRepository.java b/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/ProgrammingExerciseTestCaseTestRepository.java index da42f6d383d3..b56c34de117c 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/ProgrammingExerciseTestCaseTestRepository.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/ProgrammingExerciseTestCaseTestRepository.java @@ -1,11 +1,8 @@ package de.tum.cit.aet.artemis.programming.test_repository; import java.util.Optional; -import java.util.Set; import org.springframework.context.annotation.Primary; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; @@ -16,18 +13,4 @@ public interface ProgrammingExerciseTestCaseTestRepository extends ProgrammingExerciseTestCaseRepository { Optional findByExerciseIdAndTestName(long exerciseId, String testName); - - /** - * Returns all test cases with the associated solution entries for a programming exercise - * - * @param exerciseId of the exercise - * @return all test cases with the associated solution entries - */ - @Query(""" - SELECT DISTINCT tc - FROM ProgrammingExerciseTestCase tc - LEFT JOIN FETCH tc.solutionEntries se - WHERE tc.exercise.id = :exerciseId - """) - Set findByExerciseIdWithSolutionEntries(@Param("exerciseId") long exerciseId); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/ProgrammingExerciseTestRepository.java b/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/ProgrammingExerciseTestRepository.java index 19c04aac1ffa..065ba6ade995 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/ProgrammingExerciseTestRepository.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/ProgrammingExerciseTestRepository.java @@ -30,17 +30,13 @@ public interface ProgrammingExerciseTestRepository extends ProgrammingExerciseRe LEFT JOIN FETCH p.templateParticipation LEFT JOIN FETCH p.solutionParticipation LEFT JOIN FETCH p.exampleSubmissions - LEFT JOIN FETCH p.exerciseHints eh - LEFT JOIN FETCH eh.solutionEntries LEFT JOIN FETCH p.tutorParticipations LEFT JOIN FETCH p.posts - LEFT JOIN FETCH p.testCases tc - LEFT JOIN FETCH tc.solutionEntries + LEFT JOIN FETCH p.testCases LEFT JOIN FETCH p.staticCodeAnalysisCategories LEFT JOIN FETCH p.auxiliaryRepositories LEFT JOIN FETCH p.tasks t LEFT JOIN FETCH t.testCases - LEFT JOIN FETCH t.exerciseHints LEFT JOIN FETCH p.plagiarismDetectionConfig LEFT JOIN FETCH p.buildConfig WHERE p.id = :exerciseId diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java index 2a35129aaa9c..c946b4c68e7a 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java @@ -126,7 +126,6 @@ private static void populateUnreleasedProgrammingExercise(ProgrammingExercise pr programmingExercise.setBuildConfig(new ProgrammingExerciseBuildConfig()); } programmingExercise.setStaticCodeAnalysisEnabled(false); - programmingExercise.getBuildConfig().setTestwiseCoverageEnabled(false); programmingExercise.setAssessmentType(AssessmentType.SEMI_AUTOMATIC); programmingExercise.setProgrammingLanguage(programmingLanguage); programmingExercise.getBuildConfig().setBuildScript("Some script"); @@ -173,7 +172,6 @@ public static ProgrammingExercise generateToBeImportedProgrammingExercise(String toBeImported.setTotalNumberOfAssessments(template.getTotalNumberOfAssessments()); toBeImported.setNumberOfComplaints(template.getNumberOfComplaints()); toBeImported.setNumberOfMoreFeedbackRequests(template.getNumberOfMoreFeedbackRequests()); - toBeImported.setExerciseHints(null); toBeImported.setSolutionParticipation(null); toBeImported.setTemplateParticipation(null); buildConfig.setSequentialTestRuns(template.getBuildConfig().hasSequentialTestRuns()); @@ -189,7 +187,6 @@ public static ProgrammingExercise generateToBeImportedProgrammingExercise(String toBeImported.setAllowOnlineEditor(template.isAllowOnlineEditor()); toBeImported.setAllowOfflineIde(template.isAllowOfflineIde()); toBeImported.setStaticCodeAnalysisEnabled(template.isStaticCodeAnalysisEnabled()); - buildConfig.setTestwiseCoverageEnabled(template.getBuildConfig().isTestwiseCoverageEnabled()); toBeImported.setTutorParticipations(null); toBeImported.setPosts(null); toBeImported.setStudentParticipations(null); @@ -242,7 +239,7 @@ public static TestResultsDTO generateTestResultDTO(String fullName, String repoN final var staticCodeAnalysisReports = enableStaticAnalysisReports ? generateStaticCodeAnalysisReports(programmingLanguage) : new ArrayList(); return new TestResultsDTO(successfulTestNames.size(), 0, 0, failedTestNames.size(), fullName, commits != null && !commits.isEmpty() ? commits : List.of(commitDTO), - List.of(testSuiteDto != null ? testSuiteDto : testSuite), staticCodeAnalysisReports, List.of(), buildRunDate != null ? buildRunDate : now(), false, logs); + List.of(testSuiteDto != null ? testSuiteDto : testSuite), staticCodeAnalysisReports, buildRunDate != null ? buildRunDate : now(), false, logs); } /** @@ -394,21 +391,20 @@ public static StaticCodeAnalysisCategory generateStaticCodeAnalysisCategory(Prog * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. */ public static void populateUnreleasedProgrammingExercise(ProgrammingExercise programmingExercise, String shortName, String title, boolean enableStaticCodeAnalysis) { - populateUnreleasedProgrammingExercise(programmingExercise, shortName, title, enableStaticCodeAnalysis, false, ProgrammingLanguage.JAVA); + populateUnreleasedProgrammingExercise(programmingExercise, shortName, title, enableStaticCodeAnalysis, ProgrammingLanguage.JAVA); } /** * Populates the provided programming exercise with the given short name, title, and other values. The release date of the exercise is set in the future. * - * @param programmingExercise The exercise to be populated. - * @param shortName The short name of the exercise. - * @param title The title of the exercise. - * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. - * @param enableTestwiseCoverageAnalysis True, if test wise coverage analysis should be enabled for the exercise. - * @param programmingLanguage The programming language used in the exercise. + * @param programmingExercise The exercise to be populated. + * @param shortName The short name of the exercise. + * @param title The title of the exercise. + * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. + * @param programmingLanguage The programming language used in the exercise. */ public static void populateUnreleasedProgrammingExercise(ProgrammingExercise programmingExercise, String shortName, String title, boolean enableStaticCodeAnalysis, - boolean enableTestwiseCoverageAnalysis, ProgrammingLanguage programmingLanguage) { + ProgrammingLanguage programmingLanguage) { programmingExercise.setProgrammingLanguage(programmingLanguage); programmingExercise.setShortName(shortName); programmingExercise.generateAndSetProjectKey(); @@ -444,7 +440,6 @@ else if (programmingLanguage == ProgrammingLanguage.C) { if (enableStaticCodeAnalysis) { programmingExercise.setMaxStaticCodeAnalysisPenalty(40); } - programmingExercise.getBuildConfig().setTestwiseCoverageEnabled(enableTestwiseCoverageAnalysis); // Note: no separators are allowed for Swift package names if (programmingLanguage == ProgrammingLanguage.SWIFT) { programmingExercise.setPackageName("swiftTest"); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseResultTestService.java b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseResultTestService.java index 1ad25c969e6c..538e2890fd0a 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseResultTestService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseResultTestService.java @@ -11,19 +11,15 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.after; import static org.mockito.Mockito.argThat; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.time.ZonedDateTime; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.eclipse.jgit.api.errors.GitAPIException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; @@ -52,13 +48,12 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCaseType; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.SolutionProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.StaticCodeAnalysisTool; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTestCaseType; -import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO; +import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification; import de.tum.cit.aet.artemis.programming.dto.ResultDTO; -import de.tum.cit.aet.artemis.programming.hestia.util.TestwiseCoverageTestUtil; import de.tum.cit.aet.artemis.programming.repository.ParticipationVCSAccessTokenRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; @@ -152,10 +147,10 @@ public void setup(String userPrefix) { } public void setupForProgrammingLanguage(ProgrammingLanguage programmingLanguage) { - course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, false, programmingLanguage); + course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, programmingLanguage); programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); programmingExerciseUtilService.addTestCasesToProgrammingExercise(programmingExercise); - programmingExerciseWithStaticCodeAnalysis = programmingExerciseUtilService.addProgrammingExerciseToCourse(course, true, false, programmingLanguage); + programmingExerciseWithStaticCodeAnalysis = programmingExerciseUtilService.addProgrammingExerciseToCourse(course, true, programmingLanguage); staticCodeAnalysisService.createDefaultCategories(programmingExerciseWithStaticCodeAnalysis); // This is done to avoid an unproxy issue in the processNewResult method of the ResultService. solutionParticipation = solutionProgrammingExerciseRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseId(programmingExercise.getId()).orElseThrow(); @@ -180,7 +175,7 @@ public void tearDown() { } // Test - public void shouldUpdateFeedbackInSemiAutomaticResult(AbstractBuildResultNotificationDTO buildResultNotification, String loginName) throws Exception { + public void shouldUpdateFeedbackInSemiAutomaticResult(BuildResultNotification buildResultNotification, String loginName) throws Exception { // Make sure we only have one participation var participations = participationRepository.findByExerciseId(programmingExercise.getId()); for (ProgrammingExerciseStudentParticipation participation : participations) { @@ -229,7 +224,7 @@ public void shouldUpdateFeedbackInSemiAutomaticResult(AbstractBuildResultNotific assertThat(semiAutoResult.getFeedbacks().get(1).getType()).isEqualTo(FeedbackType.AUTOMATIC); } - private void postResult(AbstractBuildResultNotificationDTO requestBodyMap) throws Exception { + private void postResult(BuildResultNotification requestBodyMap) throws Exception { final var alteredObj = convertBuildResultToJsonObject(requestBodyMap); HttpHeaders httpHeaders = new HttpHeaders(); @@ -237,7 +232,7 @@ private void postResult(AbstractBuildResultNotificationDTO requestBodyMap) throw request.postWithoutLocation("/api/public/programming-exercises/new-result", alteredObj, HttpStatus.OK, httpHeaders); } - public static Object convertBuildResultToJsonObject(AbstractBuildResultNotificationDTO requestBodyMap) { + public static Object convertBuildResultToJsonObject(BuildResultNotification requestBodyMap) { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); return mapper.convertValue(requestBodyMap, Object.class); @@ -255,7 +250,7 @@ private ProgrammingExerciseTestCase createTest(String testName, long testId, Pro } // Test - public void shouldUpdateTestCasesAndResultScoreFromSolutionParticipationResult(AbstractBuildResultNotificationDTO resultNotification, boolean withFailedTest) { + public void shouldUpdateTestCasesAndResultScoreFromSolutionParticipationResult(BuildResultNotification resultNotification, boolean withFailedTest) { // reset saved test weights to be all 1 var test2 = programmingExerciseTestCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "test2").orElseThrow(); var test3 = programmingExerciseTestCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "test3").orElseThrow(); @@ -285,8 +280,7 @@ public void shouldUpdateTestCasesAndResultScoreFromSolutionParticipationResult(A // test1 - test3 already exist, test4 should be newly created now. // All tests must have active = true since they are now used in the new solution result - assertThat(testCases).usingRecursiveFieldByFieldElementComparatorIgnoringFields("exercise", "id", "tasks", "solutionEntries", "coverageEntries") - .containsExactlyInAnyOrderElementsOf(expectedTestCases); + assertThat(testCases).usingRecursiveFieldByFieldElementComparatorIgnoringFields("exercise", "id", "tasks").containsExactlyInAnyOrderElementsOf(expectedTestCases); assertThat(result).isNotNull(); if (withFailedTest) { @@ -306,7 +300,7 @@ public void shouldUpdateTestCasesAndResultScoreFromSolutionParticipationResult(A } // Test - public void shouldStoreFeedbackForResultWithStaticCodeAnalysisReport(AbstractBuildResultNotificationDTO resultNotification, ProgrammingLanguage programmingLanguage) { + public void shouldStoreFeedbackForResultWithStaticCodeAnalysisReport(BuildResultNotification resultNotification, ProgrammingLanguage programmingLanguage) { final long participationId = programmingExerciseStudentParticipationStaticCodeAnalysis.getId(); final var resultRequestBody = convertBuildResultToJsonObject(resultNotification); final var result = gradingService.processNewProgrammingExerciseResult(programmingExerciseStudentParticipationStaticCodeAnalysis, resultRequestBody); @@ -331,7 +325,7 @@ public void shouldStoreFeedbackForResultWithStaticCodeAnalysisReport(AbstractBui } // Test - public void shouldGenerateNewManualResultIfManualAssessmentExists(AbstractBuildResultNotificationDTO resultNotification) { + public void shouldGenerateNewManualResultIfManualAssessmentExists(BuildResultNotification resultNotification) { activateFourTests(); var programmingSubmission = programmingExerciseUtilService.createProgrammingSubmission(programmingExerciseStudentParticipation, false); @@ -360,7 +354,7 @@ public void shouldGenerateNewManualResultIfManualAssessmentExists(AbstractBuildR } // Test - public void shouldRejectNotificationWithoutCommitHash(AbstractBuildResultNotificationDTO resultNotification) { + public void shouldRejectNotificationWithoutCommitHash(BuildResultNotification resultNotification) { final Object resultRequestBody = convertBuildResultToJsonObject(resultNotification); assertThatThrownBy(() -> gradingService.processNewProgrammingExerciseResult(programmingExerciseStudentParticipation, resultRequestBody)) .isInstanceOf(IllegalArgumentException.class).hasMessageContaining("does not specify the assignment commit hash."); @@ -375,36 +369,7 @@ private void activateFourTests() { } // Test - public void shouldGenerateTestwiseCoverageFileReports(AbstractBuildResultNotificationDTO resultNotification) throws GitAPIException { - // set testwise coverage analysis for programming exercise - programmingExercise.getBuildConfig().setTestwiseCoverageEnabled(true); - programmingExerciseBuildConfigRepository.save(programmingExercise.getBuildConfig()); - programmingExerciseRepository.save(programmingExercise); - solutionParticipation.setProgrammingExercise(programmingExercise); - solutionProgrammingExerciseRepository.save(solutionParticipation); - programmingExerciseUtilService.createProgrammingSubmission(solutionParticipation, false); - - // setup mocks - doReturn(null).when(gitService).getOrCheckoutRepository(any(), eq(true)); - doNothing().when(gitService).resetToOriginHead(any()); - doNothing().when(gitService).pullIgnoreConflicts(any()); - doReturn(Collections.emptyMap()).when(gitService).listFilesAndFolders(any()); - - var expectedReportsByTestName = TestwiseCoverageTestUtil.generateCoverageFileReportByTestName(); - - final var resultRequestBody = convertBuildResultToJsonObject(resultNotification); - final var result = gradingService.processNewProgrammingExerciseResult(solutionParticipation, resultRequestBody); - assertThat(result).isNotNull(); - var actualReportsByTestName = result.getCoverageFileReportsByTestCaseName(); - assertThat(actualReportsByTestName).usingRecursiveComparison().isEqualTo(expectedReportsByTestName); - - // the coverage result attribute is transient in the result and should not be saved to the database - var resultFromDatabase = resultRepository.findByIdElseThrow(result.getId()); - assertThat(resultFromDatabase.getCoverageFileReportsByTestCaseName()).isNull(); - } - - // Test - public void shouldIgnoreResultIfNotOnDefaultBranch(AbstractBuildResultNotificationDTO resultNotification) { + public void shouldIgnoreResultIfNotOnDefaultBranch(BuildResultNotification resultNotification) { solutionParticipation.setProgrammingExercise(programmingExercise); final var resultRequestBody = convertBuildResultToJsonObject(resultNotification); @@ -413,7 +378,7 @@ public void shouldIgnoreResultIfNotOnDefaultBranch(AbstractBuildResultNotificati } // Test - public void shouldCreateResultOnParticipationDefaultBranch(AbstractBuildResultNotificationDTO resultNotification) { + public void shouldCreateResultOnParticipationDefaultBranch(BuildResultNotification resultNotification) { programmingExerciseStudentParticipation.setProgrammingExercise(programmingExercise); programmingExerciseStudentParticipation.setBranch("branch"); @@ -424,7 +389,7 @@ public void shouldCreateResultOnParticipationDefaultBranch(AbstractBuildResultNo } // Test - public void shouldIgnoreResultIfNotOnParticipationBranch(AbstractBuildResultNotificationDTO resultNotification) { + public void shouldIgnoreResultIfNotOnParticipationBranch(BuildResultNotification resultNotification) { programmingExerciseStudentParticipation.setBranch("default"); programmingExerciseStudentParticipation.setProgrammingExercise(programmingExercise); @@ -434,7 +399,7 @@ public void shouldIgnoreResultIfNotOnParticipationBranch(AbstractBuildResultNoti } // Test - public void shouldCreateResultOnCustomDefaultBranch(String defaultBranch, AbstractBuildResultNotificationDTO resultNotification) { + public void shouldCreateResultOnCustomDefaultBranch(String defaultBranch, BuildResultNotification resultNotification) { programmingExercise.getBuildConfig().setBranch(defaultBranch); programmingExerciseBuildConfigRepository.save(programmingExercise.getBuildConfig()); programmingExercise = programmingExerciseRepository.save(programmingExercise); @@ -447,8 +412,7 @@ public void shouldCreateResultOnCustomDefaultBranch(String defaultBranch, Abstra } // Test - public void shouldCorrectlyNotifyStudentsAboutNewResults(AbstractBuildResultNotificationDTO resultNotification, WebsocketMessagingService websocketMessagingService) - throws Exception { + public void shouldCorrectlyNotifyStudentsAboutNewResults(BuildResultNotification resultNotification, WebsocketMessagingService websocketMessagingService) throws Exception { programmingExerciseUtilService.addTestCasesToProgrammingExercise(programmingExercise); var programmingSubmission = programmingExerciseUtilService.createProgrammingSubmission(programmingExerciseStudentParticipation, false); @@ -471,8 +435,7 @@ public void shouldCorrectlyNotifyStudentsAboutNewResults(AbstractBuildResultNoti } // Test - public void shouldNotNotifyStudentsAboutNewResults(AbstractBuildResultNotificationDTO resultNotification, WebsocketMessagingService websocketMessagingService) - throws Exception { + public void shouldNotNotifyStudentsAboutNewResults(BuildResultNotification resultNotification, WebsocketMessagingService websocketMessagingService) throws Exception { programmingExerciseUtilService.addTestCasesToProgrammingExercise(programmingExercise); var programmingSubmission = programmingExerciseUtilService.createProgrammingSubmission(programmingExerciseStudentParticipation, false); @@ -486,7 +449,7 @@ public void shouldNotNotifyStudentsAboutNewResults(AbstractBuildResultNotificati } // Test - public void shouldRemoveTestCaseNamesFromWebsocketNotification(AbstractBuildResultNotificationDTO resultNotification, WebsocketMessagingService websocketMessagingService) + public void shouldRemoveTestCaseNamesFromWebsocketNotification(BuildResultNotification resultNotification, WebsocketMessagingService websocketMessagingService) throws Exception { var programmingSubmission = programmingExerciseUtilService.createProgrammingSubmission(programmingExerciseStudentParticipation, false); programmingExerciseStudentParticipation.addSubmission(programmingSubmission); @@ -500,7 +463,7 @@ public void shouldRemoveTestCaseNamesFromWebsocketNotification(AbstractBuildResu } // Test - public void shouldUpdateParticipantScoresOnlyOnce(AbstractBuildResultNotificationDTO resultNotification, InstanceMessageSendService instanceMessageSendService) { + public void shouldUpdateParticipantScoresOnlyOnce(BuildResultNotification resultNotification, InstanceMessageSendService instanceMessageSendService) { final var resultRequestBody = convertBuildResultToJsonObject(resultNotification); gradingService.processNewProgrammingExerciseResult(programmingExerciseStudentParticipation, resultRequestBody); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java index e097a79f772f..eb0268471ccf 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java @@ -120,6 +120,7 @@ import de.tum.cit.aet.artemis.programming.domain.AuxiliaryRepository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; @@ -128,8 +129,6 @@ import de.tum.cit.aet.artemis.programming.domain.StaticCodeAnalysisCategory; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogStatisticsEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.LockRepositoryPolicy; import de.tum.cit.aet.artemis.programming.dto.BuildLogStatisticsDTO; import de.tum.cit.aet.artemis.programming.repository.AuxiliaryRepositoryRepository; @@ -784,7 +783,6 @@ public void createAndImportJavaProgrammingExercise(boolean staticCodeAnalysisEna sourceExercise.setTasks(Collections.singletonList(task)); programmingExerciseTaskRepository.save(task); programmingExerciseRepository.save(sourceExercise); - programmingExerciseUtilService.addHintsToExercise(sourceExercise); // Reset because we will add mocks for new requests mockDelegate.resetMockProvider(); @@ -819,7 +817,8 @@ public void createAndImportJavaProgrammingExercise(boolean staticCodeAnalysisEna importedExercise = programmingExerciseUtilService.loadProgrammingExerciseWithEagerReferences(importedExercise); // Check that the tasks were imported correctly (see #5474) - assertThat(programmingExerciseTaskRepository.findByExerciseId(importedExercise.getId())).hasSameSizeAs(sourceExercise.getTasks()); + var importedExerciseTasks = programmingExerciseTaskRepository.findByExerciseId(importedExercise.getId()); + assertThat(importedExerciseTasks).hasSameSizeAs(sourceExercise.getTasks()); } // TEST @@ -828,10 +827,9 @@ public void importExercise_created(ProgrammingLanguage programmingLanguage, bool // Setup exercises for import ProgrammingExercise sourceExercise = programmingExerciseUtilService.addCourseWithOneProgrammingExerciseAndStaticCodeAnalysisCategories(programmingLanguage); sourceExercise.setPlagiarismDetectionConfig(PlagiarismDetectionConfig.createDefault()); - sourceExercise = programmingExerciseRepository.save(sourceExercise); sourceExercise.setStaticCodeAnalysisEnabled(staticCodeAnalysisEnabled); + sourceExercise = programmingExerciseRepository.save(sourceExercise); programmingExerciseUtilService.addTestCasesToProgrammingExercise(sourceExercise); - programmingExerciseUtilService.addHintsToExercise(sourceExercise); sourceExercise = programmingExerciseUtilService.loadProgrammingExerciseWithEagerReferences(sourceExercise); ProgrammingExercise exerciseToBeImported = ProgrammingExerciseFactory.generateToBeImportedProgrammingExercise("ImportTitle", "imported", sourceExercise, courseUtilService.addEmptyCourse()); @@ -868,15 +866,7 @@ public void importExercise_created(ProgrammingLanguage programmingLanguage, bool var sourceTestCaseIds = sourceExercise.getTestCases().stream().map(ProgrammingExerciseTestCase::getId).collect(Collectors.toSet()); assertThat(importedTestCaseIds).doesNotContainAnyElementsOf(sourceTestCaseIds); assertThat(importedExercise.getTestCases()).usingRecursiveFieldByFieldElementComparator() - .usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "exercise", "tasks", "solutionEntries", "coverageEntries") - .containsExactlyInAnyOrderElementsOf(sourceExercise.getTestCases()); - - // Assert correct creation of hints - var importedHintIds = importedExercise.getExerciseHints().stream().map(ExerciseHint::getId).collect(Collectors.toSet()); - var sourceHintIds = sourceExercise.getExerciseHints().stream().map(ExerciseHint::getId).collect(Collectors.toSet()); - assertThat(importedHintIds).doesNotContainAnyElementsOf(sourceHintIds); - assertThat(importedExercise.getExerciseHints()).usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "exercise", "exerciseHintActivations") - .containsExactlyInAnyOrderElementsOf(sourceExercise.getExerciseHints()); + .usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "exercise", "tasks").containsExactlyInAnyOrderElementsOf(sourceExercise.getTestCases()); // Assert creation of new build plan ids assertThat(importedExercise.getSolutionParticipation().getBuildPlanId()).isNotBlank().isNotEqualTo(sourceExercise.getSolutionParticipation().getBuildPlanId()); @@ -895,7 +885,6 @@ public void updateBuildPlanURL() throws Exception { sourceExercise.setStaticCodeAnalysisEnabled(staticCodeAnalysisEnabled); sourceExercise.getBuildConfig().generateAndSetBuildPlanAccessSecret(); programmingExerciseUtilService.addTestCasesToProgrammingExercise(sourceExercise); - programmingExerciseUtilService.addHintsToExercise(sourceExercise); programmingExerciseBuildConfigRepository.save(sourceExercise.getBuildConfig()); sourceExercise = programmingExerciseUtilService.loadProgrammingExerciseWithEagerReferences(sourceExercise); ProgrammingExercise exerciseToBeImported = ProgrammingExerciseFactory.generateToBeImportedProgrammingExercise("ImportTitle", "imported", sourceExercise, @@ -979,7 +968,6 @@ public void testImportProgrammingExercise_team_modeChange() throws Exception { ProgrammingExercise sourceExercise = programmingExerciseUtilService.addCourseWithOneProgrammingExerciseAndStaticCodeAnalysisCategories(); sourceExercise.setMode(ExerciseMode.INDIVIDUAL); programmingExerciseUtilService.addTestCasesToProgrammingExercise(sourceExercise); - programmingExerciseUtilService.addHintsToExercise(sourceExercise); sourceExercise = programmingExerciseUtilService.loadProgrammingExerciseWithEagerReferences(sourceExercise); sourceExercise.setCourse(sourceExercise.getCourseViaExerciseGroupOrCourseMember()); programmingExerciseRepository.save(sourceExercise); @@ -1019,7 +1007,7 @@ public void testImportProgrammingExercise_individual_modeChange() throws Excepti ProgrammingExercise sourceExercise = programmingExerciseUtilService.addCourseWithOneProgrammingExerciseAndStaticCodeAnalysisCategories(); sourceExercise.setMode(TEAM); programmingExerciseUtilService.addTestCasesToProgrammingExercise(sourceExercise); - programmingExerciseUtilService.addHintsToExercise(sourceExercise); + programmingExerciseRepository.save(sourceExercise); sourceExercise = programmingExerciseUtilService.loadProgrammingExerciseWithEagerReferences(sourceExercise); var teamAssignmentConfig = new TeamAssignmentConfig(); teamAssignmentConfig.setExercise(sourceExercise); @@ -1186,7 +1174,6 @@ public void importProgrammingExerciseAsPartOfExamImport() throws Exception { ProgrammingExercise sourceExercise = programmingExerciseUtilService.addProgrammingExerciseToExam(sourceExam, 0); sourceExercise.setStaticCodeAnalysisEnabled(false); programmingExerciseUtilService.addTestCasesToProgrammingExercise(sourceExercise); - programmingExerciseUtilService.addHintsToExercise(sourceExercise); sourceExercise = programmingExerciseUtilService.loadProgrammingExerciseWithEagerReferences(sourceExercise); // Setup to be imported exam and exercise @@ -1231,15 +1218,7 @@ public void importProgrammingExerciseAsPartOfExamImport() throws Exception { var sourceTestCaseIds = sourceExercise.getTestCases().stream().map(ProgrammingExerciseTestCase::getId).toList(); assertThat(importedTestCaseIds).doesNotContainAnyElementsOf(sourceTestCaseIds); assertThat(importedExercise.getTestCases()).usingRecursiveFieldByFieldElementComparator() - .usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "exercise", "tasks", "solutionEntries", "coverageEntries") - .containsExactlyInAnyOrderElementsOf(sourceExercise.getTestCases()); - - // Assert correct creation of hints - var importedHintIds = importedExercise.getExerciseHints().stream().map(ExerciseHint::getId).toList(); - var sourceHintIds = sourceExercise.getExerciseHints().stream().map(ExerciseHint::getId).toList(); - assertThat(importedHintIds).doesNotContainAnyElementsOf(sourceHintIds); - assertThat(importedExercise.getExerciseHints()).usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "exercise", "exerciseHintActivations") - .containsExactlyInAnyOrderElementsOf(sourceExercise.getExerciseHints()); + .usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "exercise", "tasks").containsExactlyInAnyOrderElementsOf(sourceExercise.getTestCases()); } // TEST diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseUtilService.java b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseUtilService.java index 7178f4376a08..2c4321d22b79 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseUtilService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseUtilService.java @@ -18,7 +18,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; @@ -52,6 +51,7 @@ import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; import de.tum.cit.aet.artemis.programming.domain.AuxiliaryRepository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; @@ -60,10 +60,6 @@ import de.tum.cit.aet.artemis.programming.domain.SolutionProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.TemplateProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.build.BuildPlanType; -import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.SubmissionPolicy; import de.tum.cit.aet.artemis.programming.repository.AuxiliaryRepositoryRepository; import de.tum.cit.aet.artemis.programming.repository.BuildPlanRepository; @@ -72,9 +68,6 @@ import de.tum.cit.aet.artemis.programming.repository.StaticCodeAnalysisCategoryRepository; import de.tum.cit.aet.artemis.programming.repository.SubmissionPolicyRepository; import de.tum.cit.aet.artemis.programming.repository.TemplateProgrammingExerciseParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CodeHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; import de.tum.cit.aet.artemis.programming.service.GitService; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTaskTestRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; @@ -142,18 +135,9 @@ public class ProgrammingExerciseUtilService { @Autowired private StudentParticipationTestRepository studentParticipationRepo; - @Autowired - private ExerciseHintRepository exerciseHintRepository; - @Autowired private ProgrammingExerciseTaskTestRepository programmingExerciseTaskRepository; - @Autowired - private ProgrammingExerciseSolutionEntryRepository solutionEntryRepository; - - @Autowired - private CodeHintRepository codeHintRepository; - @Autowired private ProgrammingExerciseTestRepository programmingExerciseTestRepository; @@ -376,7 +360,7 @@ public Course addCourseWithOneProgrammingExercise() { * @return The created course with a programming exercise. */ public Course addCourseWithOneProgrammingExercise(boolean enableStaticCodeAnalysis) { - return addCourseWithOneProgrammingExercise(enableStaticCodeAnalysis, false, ProgrammingLanguage.JAVA); + return addCourseWithOneProgrammingExercise(enableStaticCodeAnalysis, ProgrammingLanguage.JAVA); } /** @@ -388,36 +372,33 @@ public Course addCourseWithOneProgrammingExercise(boolean enableStaticCodeAnalys * @return The created course with a programming exercise. */ public Course addCourseWithOneProgrammingExercise(boolean enableStaticCodeAnalysis, String title, String shortName) { - return addCourseWithOneProgrammingExercise(enableStaticCodeAnalysis, false, ProgrammingLanguage.JAVA, title, shortName); + return addCourseWithOneProgrammingExercise(enableStaticCodeAnalysis, ProgrammingLanguage.JAVA, title, shortName); } /** * Creates and saves a course with a programming exercise. Uses Programming as the title and TSTEXC as the short name of the exercise. * - * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. - * @param enableTestwiseCoverageAnalysis True, if test wise coverage analysis should be enabled for the exercise. - * @param programmingLanguage The programming language fo the exercise. + * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. + * @param programmingLanguage The programming language fo the exercise. * @return The created course with a programming exercise. */ - public Course addCourseWithOneProgrammingExercise(boolean enableStaticCodeAnalysis, boolean enableTestwiseCoverageAnalysis, ProgrammingLanguage programmingLanguage) { - return addCourseWithOneProgrammingExercise(enableStaticCodeAnalysis, enableTestwiseCoverageAnalysis, programmingLanguage, "Programming", "TSTEXC"); + public Course addCourseWithOneProgrammingExercise(boolean enableStaticCodeAnalysis, ProgrammingLanguage programmingLanguage) { + return addCourseWithOneProgrammingExercise(enableStaticCodeAnalysis, programmingLanguage, "Programming", "TSTEXC"); } /** * Creates and saves a course with a programming exercise. * - * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. - * @param enableTestwiseCoverageAnalysis True, if test wise coverage analysis should be enabled for the exercise. - * @param programmingLanguage The programming language fo the exercise. - * @param title The title of the exercise. - * @param shortName The short name of the exercise. + * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. + * @param programmingLanguage The programming language fo the exercise. + * @param title The title of the exercise. + * @param shortName The short name of the exercise. * @return The created course with a programming exercise. */ - public Course addCourseWithOneProgrammingExercise(boolean enableStaticCodeAnalysis, boolean enableTestwiseCoverageAnalysis, ProgrammingLanguage programmingLanguage, - String title, String shortName) { + public Course addCourseWithOneProgrammingExercise(boolean enableStaticCodeAnalysis, ProgrammingLanguage programmingLanguage, String title, String shortName) { var course = CourseFactory.generateCourse(null, PAST_TIMESTAMP, FUTURE_FUTURE_TIMESTAMP, new HashSet<>(), "tumuser", "tutor", "editor", "instructor"); course = courseRepo.save(course); - addProgrammingExerciseToCourse(course, enableStaticCodeAnalysis, enableTestwiseCoverageAnalysis, programmingLanguage, title, shortName, null); + addProgrammingExerciseToCourse(course, enableStaticCodeAnalysis, programmingLanguage, title, shortName, null); course = courseRepo.findByIdWithExercisesAndExerciseDetailsAndLecturesElseThrow(course.getId()); for (var exercise : course.getExercises()) { if (exercise instanceof ProgrammingExercise) { @@ -446,7 +427,7 @@ public ProgrammingExercise addProgrammingExerciseToCourse(Course course) { * @return The programming exercise which was added to the course. */ public ProgrammingExercise addProgrammingExerciseToCourse(Course course, boolean enableStaticCodeAnalysis) { - return addProgrammingExerciseToCourse(course, enableStaticCodeAnalysis, false, ProgrammingLanguage.JAVA); + return addProgrammingExerciseToCourse(course, enableStaticCodeAnalysis, ProgrammingLanguage.JAVA); } /** @@ -458,56 +439,51 @@ public ProgrammingExercise addProgrammingExerciseToCourse(Course course, boolean * @return The programming exercise which was added to the course. */ public ProgrammingExercise addProgrammingExerciseToCourse(Course course, boolean enableStaticCodeAnalysis, ZonedDateTime assessmentDueDate) { - return addProgrammingExerciseToCourse(course, enableStaticCodeAnalysis, false, ProgrammingLanguage.JAVA, assessmentDueDate); + return addProgrammingExerciseToCourse(course, enableStaticCodeAnalysis, ProgrammingLanguage.JAVA, assessmentDueDate); } /** * Adds a programming exercise to the given course. Uses Programming as the title and TSTEXC as the short name of the exercise. * - * @param course The course to which the exercise should be added. - * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. - * @param enableTestwiseCoverageAnalysis True, if test wise coverage analysis should be enabled for the exercise. - * @param programmingLanguage The programming language used in the exercise. - * @param assessmentDueDate The assessment due date of the exercise. + * @param course The course to which the exercise should be added. + * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. + * @param programmingLanguage The programming language used in the exercise. + * @param assessmentDueDate The assessment due date of the exercise. * @return The programming exercise which was added to the course. */ - public ProgrammingExercise addProgrammingExerciseToCourse(Course course, boolean enableStaticCodeAnalysis, boolean enableTestwiseCoverageAnalysis, - ProgrammingLanguage programmingLanguage, ZonedDateTime assessmentDueDate) { - return addProgrammingExerciseToCourse(course, enableStaticCodeAnalysis, enableTestwiseCoverageAnalysis, programmingLanguage, "Programming", "TSTEXC", assessmentDueDate); + public ProgrammingExercise addProgrammingExerciseToCourse(Course course, boolean enableStaticCodeAnalysis, ProgrammingLanguage programmingLanguage, + ZonedDateTime assessmentDueDate) { + return addProgrammingExerciseToCourse(course, enableStaticCodeAnalysis, programmingLanguage, "Programming", "TSTEXC", assessmentDueDate); } /** * Adds a programming exercise without an assessment due date to the given course. Uses Programming as the title and TSTEXC as the short name of the * exercise. * - * @param course The course to which the exercise should be added. - * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. - * @param enableTestwiseCoverageAnalysis True, if test wise coverage analysis should be enabled for the exercise. - * @param programmingLanguage The programming language used in the exercise. + * @param course The course to which the exercise should be added. + * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. + * @param programmingLanguage The programming language used in the exercise. * @return The programming exercise which was added to the course. */ - public ProgrammingExercise addProgrammingExerciseToCourse(Course course, boolean enableStaticCodeAnalysis, boolean enableTestwiseCoverageAnalysis, - ProgrammingLanguage programmingLanguage) { - return addProgrammingExerciseToCourse(course, enableStaticCodeAnalysis, enableTestwiseCoverageAnalysis, programmingLanguage, "Programming", "TSTEXC", null); + public ProgrammingExercise addProgrammingExerciseToCourse(Course course, boolean enableStaticCodeAnalysis, ProgrammingLanguage programmingLanguage) { + return addProgrammingExerciseToCourse(course, enableStaticCodeAnalysis, programmingLanguage, "Programming", "TSTEXC", null); } /** * Adds a programming exercise to the given course. * - * @param course The course to which the exercise should be added. - * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. - * @param enableTestwiseCoverageAnalysis True, if test wise coverage analysis should be enabled for the exercise. - * @param programmingLanguage The programming language used in the exercise. - * @param title The title of the exercise. - * @param shortName The short name of the exercise. - * @param assessmentDueDate The assessment due date of the exercise. + * @param course The course to which the exercise should be added. + * @param enableStaticCodeAnalysis True, if the static code analysis should be enabled for the exercise. + * @param programmingLanguage The programming language used in the exercise. + * @param title The title of the exercise. + * @param shortName The short name of the exercise. + * @param assessmentDueDate The assessment due date of the exercise. * @return The programming exercise which was added to the course. */ - public ProgrammingExercise addProgrammingExerciseToCourse(Course course, boolean enableStaticCodeAnalysis, boolean enableTestwiseCoverageAnalysis, - ProgrammingLanguage programmingLanguage, String title, String shortName, ZonedDateTime assessmentDueDate) { + public ProgrammingExercise addProgrammingExerciseToCourse(Course course, boolean enableStaticCodeAnalysis, ProgrammingLanguage programmingLanguage, String title, + String shortName, ZonedDateTime assessmentDueDate) { var programmingExercise = (ProgrammingExercise) new ProgrammingExercise().course(course); - ProgrammingExerciseFactory.populateUnreleasedProgrammingExercise(programmingExercise, shortName, title, enableStaticCodeAnalysis, enableTestwiseCoverageAnalysis, - programmingLanguage); + ProgrammingExerciseFactory.populateUnreleasedProgrammingExercise(programmingExercise, shortName, title, enableStaticCodeAnalysis, programmingLanguage); programmingExercise.setAssessmentDueDate(assessmentDueDate); programmingExercise.setPresentationScoreEnabled(course.getPresentationScore() != 0); @@ -544,27 +520,6 @@ public Course addCourseWithNamedProgrammingExercise(String programmingExerciseTi return courseRepo.findByIdWithExercisesAndExerciseDetailsAndLecturesElseThrow(course.getId()); } - /** - * Creates and saves a course with a programming exercise and 3 active, always visible test cases with different weights. - * - * @return The newly created course with a programming exercise. - */ - public Course addCourseWithOneProgrammingExerciseAndSpecificTestCases() { - Course course = addCourseWithOneProgrammingExercise(); - ProgrammingExercise programmingExercise = exerciseUtilService.findProgrammingExerciseWithTitle(course.getExercises(), "Programming"); - - List testCases = new ArrayList<>(); - testCases.add(new ProgrammingExerciseTestCase().testName("testClass[BubbleSort]").weight(1.0).active(true).exercise(programmingExercise).bonusMultiplier(1D).bonusPoints(0D) - .visibility(Visibility.ALWAYS)); - testCases.add(new ProgrammingExerciseTestCase().testName("testMethods[Context]").weight(2.0).active(true).exercise(programmingExercise).bonusMultiplier(1D).bonusPoints(0D) - .visibility(Visibility.ALWAYS)); - testCases.add(new ProgrammingExerciseTestCase().testName("testMethods[Policy]").weight(3.0).active(true).exercise(programmingExercise).bonusMultiplier(1D).bonusPoints(0D) - .visibility(Visibility.ALWAYS)); - testCaseRepository.saveAll(testCases); - - return courseRepo.findByIdWithEagerExercisesElseThrow(course.getId()); - } - /** * Creates and saves a course with a java programming exercise with static code analysis enabled. * @@ -581,7 +536,7 @@ public ProgrammingExercise addCourseWithOneProgrammingExerciseAndStaticCodeAnaly * @return The newly created programming exercise. */ public ProgrammingExercise addCourseWithOneProgrammingExerciseAndStaticCodeAnalysisCategories(ProgrammingLanguage programmingLanguage) { - Course course = addCourseWithOneProgrammingExercise(true, false, programmingLanguage); + Course course = addCourseWithOneProgrammingExercise(true, programmingLanguage); ProgrammingExercise programmingExercise = exerciseUtilService.findProgrammingExerciseWithTitle(course.getExercises(), "Programming"); programmingExercise = programmingExerciseRepository.save(programmingExercise); programmingExercise = programmingExerciseRepository.findWithBuildConfigById(programmingExercise.getId()).orElseThrow(); @@ -863,27 +818,6 @@ public ProgrammingSubmission addProgrammingSubmissionToResultAndParticipation(Re return submissionRepository.save(submission); } - /** - * Adds 3 hints to the given programming exercise. Each hint has a unique content and title. All have a display threshold of 3. - * - * @param exercise The exercise to which hints should be added. - */ - public void addHintsToExercise(ProgrammingExercise exercise) { - ExerciseHint exerciseHint1 = new ExerciseHint().content("content 1").exercise(exercise).title("title 1"); - ExerciseHint exerciseHint2 = new ExerciseHint().content("content 2").exercise(exercise).title("title 2"); - ExerciseHint exerciseHint3 = new ExerciseHint().content("content 3").exercise(exercise).title("title 3"); - exerciseHint1.setDisplayThreshold((short) 3); - exerciseHint2.setDisplayThreshold((short) 3); - exerciseHint3.setDisplayThreshold((short) 3); - Set hints = new HashSet<>(); - hints.add(exerciseHint1); - hints.add(exerciseHint2); - hints.add(exerciseHint3); - exercise.setExerciseHints(hints); - exerciseHintRepository.saveAll(hints); - programmingExerciseRepository.save(exercise); - } - /** * Adds a task for each test case and adds it to the problem statement of the programming exercise. * @@ -909,48 +843,6 @@ public void addTasksToProgrammingExercise(ProgrammingExercise programmingExercis programmingExerciseRepository.save(programmingExercise); } - /** - * Adds a solution entry to each test case of the given programming exercise. - * - * @param programmingExercise The exercise to which solution entries should be added. - */ - public void addSolutionEntriesToProgrammingExercise(ProgrammingExercise programmingExercise) { - for (ProgrammingExerciseTestCase testCase : programmingExercise.getTestCases()) { - var solutionEntry = new ProgrammingExerciseSolutionEntry(); - solutionEntry.setFilePath("test.txt"); - solutionEntry.setLine(1); - solutionEntry.setCode("Line for " + testCase.getTestName()); - solutionEntry.setTestCase(testCase); - - testCase.setSolutionEntries(Collections.singleton(solutionEntry)); - solutionEntryRepository.save(solutionEntry); - } - } - - /** - * Adds a code hint to each task of the given programming exercise. - * - * @param programmingExercise The programming exercise to which code hints should be added. - */ - public void addCodeHintsToProgrammingExercise(ProgrammingExercise programmingExercise) { - for (ProgrammingExerciseTask task : programmingExercise.getTasks()) { - var solutionEntries = task.getTestCases().stream().flatMap(testCase -> testCase.getSolutionEntries().stream()).collect(Collectors.toSet()); - var codeHint = new CodeHint(); - codeHint.setTitle("Code Hint for " + task.getTaskName()); - codeHint.setContent("Content for " + task.getTaskName()); - codeHint.setExercise(programmingExercise); - codeHint.setSolutionEntries(solutionEntries); - codeHint.setProgrammingExerciseTask(task); - - programmingExercise.getExerciseHints().add(codeHint); - codeHint = codeHintRepository.save(codeHint); - for (ProgrammingExerciseSolutionEntry solutionEntry : solutionEntries) { - solutionEntry.setCodeHint(codeHint); - solutionEntryRepository.save(solutionEntry); - } - } - } - /** * Loads a programming exercise with eager references from the repository. * diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingSubmissionAndResultIntegrationTestService.java b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingSubmissionAndResultIntegrationTestService.java index 3a53d1b77ada..992f9ac6e8b5 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingSubmissionAndResultIntegrationTestService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingSubmissionAndResultIntegrationTestService.java @@ -55,7 +55,7 @@ public class ProgrammingSubmissionAndResultIntegrationTestService { public ProgrammingExerciseParticipation participation; public void setUp_shouldSetSubmissionDateForBuildCorrectlyIfOnlyOnePushIsReceived(String userPrefix) { - Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, false, JAVA); + Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, JAVA); programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); programmingExercise = programmingExerciseRepository.findWithEagerStudentParticipationsStudentAndLegalSubmissionsById(programmingExercise.getId()).orElseThrow(); participation = participationUtilService.addStudentParticipationForProgrammingExercise(programmingExercise, userPrefix + "student1"); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/util/HestiaUtilTestService.java b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingUtilTestService.java similarity index 96% rename from src/test/java/de/tum/cit/aet/artemis/programming/hestia/util/HestiaUtilTestService.java rename to src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingUtilTestService.java index 92e857ca6eea..52a58e3f8040 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/util/HestiaUtilTestService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingUtilTestService.java @@ -1,5 +1,6 @@ -package de.tum.cit.aet.artemis.programming.hestia.util; +package de.tum.cit.aet.artemis.programming.util; +import static de.tum.cit.aet.artemis.programming.service.AbstractGitService.linkRepositoryForExistingGit; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; @@ -31,12 +32,9 @@ import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseStudentParticipationTestRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; -import de.tum.cit.aet.artemis.programming.util.GitUtilService; -import de.tum.cit.aet.artemis.programming.util.LocalRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; /** - * Utility service specifically used for testing Hestia related functionality. + * Utility service specifically used for testing programming exercises. * This currently includes: * - Setting up a template repository * - Setting up a solution repository @@ -45,7 +43,7 @@ * In the future this service will be extended to make testing of the code hint generation easier. */ @Service -public class HestiaUtilTestService { +public class ProgrammingUtilTestService { @Autowired private GitService gitService; @@ -237,7 +235,7 @@ public ProgrammingSubmission setupSubmission(Map files, Programm anyBoolean()); var participation = participationUtilService.addStudentParticipationForProgrammingExerciseForLocalRepo(exercise, login, participationRepo.localRepoFile.toURI()); - doReturn(gitService.linkRepositoryForExistingGit(participationRepo.originRepoFile.toPath(), null, "main", true)).when(gitService).getBareRepository(any()); + doReturn(linkRepositoryForExistingGit(participationRepo.originRepoFile.toPath(), null, "main", true)).when(gitService).getBareRepository(any()); var submission = ParticipationFactory.generateProgrammingSubmission(true, commitsList.getFirst().getId().getName(), SubmissionType.MANUAL); participation = programmingExerciseStudentParticipationRepository .findWithSubmissionsByExerciseIdAndParticipationIds(exercise.getId(), Collections.singletonList(participation.getId())).getFirst(); diff --git a/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleServiceArchitectureTest.java b/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleServiceArchitectureTest.java index cd43dd981e51..c242dfdb768b 100644 --- a/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleServiceArchitectureTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleServiceArchitectureTest.java @@ -3,7 +3,6 @@ import static com.tngtech.archunit.core.domain.JavaModifier.ABSTRACT; import static com.tngtech.archunit.core.domain.JavaModifier.FINAL; import static com.tngtech.archunit.lang.SimpleConditionEvent.violated; -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; import org.junit.jupiter.api.Test; import org.springframework.scheduling.annotation.Async; @@ -22,8 +21,8 @@ import de.tum.cit.aet.artemis.core.security.DomainUserDetailsService; import de.tum.cit.aet.artemis.core.security.jwt.JWTCookieService; import de.tum.cit.aet.artemis.lti.service.OAuth2JWKSService; +import de.tum.cit.aet.artemis.programming.service.GitDiffReportParserService; import de.tum.cit.aet.artemis.programming.service.localci.LocalCIWebsocketMessagingService; -import de.tum.cit.aet.artemis.programming.web.GitDiffReportParserService; import de.tum.cit.aet.artemis.shared.architecture.AbstractArchitectureTest; public abstract class AbstractModuleServiceArchitectureTest extends AbstractArchitectureTest implements ModuleArchitectureTest { diff --git a/src/test/javascript/spec/component/code-editor/code-editor-student-container.component.spec.ts b/src/test/javascript/spec/component/code-editor/code-editor-student-container.component.spec.ts index abe6ded99d34..2fef51c01555 100644 --- a/src/test/javascript/spec/component/code-editor/code-editor-student-container.component.spec.ts +++ b/src/test/javascript/spec/component/code-editor/code-editor-student-container.component.spec.ts @@ -10,7 +10,6 @@ import { MockProgrammingExerciseParticipationService } from '../../helpers/mocks import { GuidedTourService } from 'app/guided-tour/guided-tour.service'; import { SubmissionPolicyService } from 'app/exercises/programming/manage/services/submission-policy.service'; import { AlertService } from 'app/core/util/alert.service'; -import { ExerciseHintService } from 'app/exercises/shared/exercise-hint/shared/exercise-hint.service'; import { of } from 'rxjs'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { ActivatedRoute } from '@angular/router'; @@ -43,7 +42,6 @@ describe('CodeEditorStudentContainerComponent', () => { MockProvider(GuidedTourService), MockProvider(SubmissionPolicyService), MockProvider(AlertService), - MockProvider(ExerciseHintService), ], }) .compileComponents() diff --git a/src/test/javascript/spec/component/exam/manage/programming-exam-diff.component.spec.ts b/src/test/javascript/spec/component/exam/manage/programming-exam-diff.component.spec.ts index 3a0d9d6e640c..ed85aa75ceb7 100644 --- a/src/test/javascript/spec/component/exam/manage/programming-exam-diff.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/programming-exam-diff.component.spec.ts @@ -6,12 +6,12 @@ import { MockComponent, MockPipe } from 'ng-mocks'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { of } from 'rxjs'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffReport } from '../../../../../../main/webapp/app/entities/programming-exercise-git-diff-report.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { MockNgbModalService } from '../../../helpers/mocks/service/mock-ngb-modal.service'; -import { GitDiffReportModalComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-report-modal.component'; -import { ProgrammingExerciseGitDiffEntry } from 'app/entities/hestia/programming-exercise-git-diff-entry.model'; +import { GitDiffReportModalComponent } from '../../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-report-modal.component'; +import { ProgrammingExerciseGitDiffEntry } from '../../../../../../main/webapp/app/entities/programming-exercise-git-diff-entry.model'; import { IncludedInScoreBadgeComponent } from 'app/exercises/shared/exercise-headers/included-in-score-badge.component'; import { CachedRepositoryFilesService } from 'app/exercises/programming/manage/services/cached-repository-files.service'; diff --git a/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint-detail.component.spec.ts b/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint-detail.component.spec.ts deleted file mode 100644 index bbc956f3715d..000000000000 --- a/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint-detail.component.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; - -import { ExerciseHintDetailComponent } from 'app/exercises/shared/exercise-hint/manage/exercise-hint-detail.component'; -import { ArtemisTestModule } from '../../../test.module'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; - -describe('ExerciseHint Management Detail Component', () => { - let comp: ExerciseHintDetailComponent; - let fixture: ComponentFixture; - const exerciseHint = new ExerciseHint(); - exerciseHint.id = 123; - const route = { data: of({ exerciseHint }), params: of({ exerciseId: 1 }) } as any as ActivatedRoute; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - declarations: [ExerciseHintDetailComponent], - providers: [{ provide: ActivatedRoute, useValue: route }], - }) - .overrideTemplate(ExerciseHintDetailComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(ExerciseHintDetailComponent); - comp = fixture.componentInstance; - }); - - describe('onInit', () => { - it('should call load all on init', () => { - // WHEN - comp.ngOnInit(); - - // THEN - expect(comp.exerciseHint).toEqual(expect.objectContaining({ id: 123 })); - }); - }); -}); diff --git a/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint-update.component.spec.ts b/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint-update.component.spec.ts deleted file mode 100644 index fe8282591d2b..000000000000 --- a/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint-update.component.spec.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { ComponentFixture, TestBed, fakeAsync, flush, tick } from '@angular/core/testing'; -import { FormBuilder, FormsModule } from '@angular/forms'; -import { of } from 'rxjs'; -import { HttpResponse } from '@angular/common/http'; - -import { ExerciseHintUpdateComponent } from 'app/exercises/shared/exercise-hint/manage/exercise-hint-update.component'; -import { ArtemisTestModule } from '../../../test.module'; -import { TranslateService } from '@ngx-translate/core'; -import { MockComponent, MockProvider } from 'ng-mocks'; -import { HelpIconComponent } from 'app/shared/components/help-icon.component'; -import { ExerciseHintService } from 'app/exercises/shared/exercise-hint/shared/exercise-hint.service'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; -import { ActivatedRoute } from '@angular/router'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; -import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; -import { CodeHint } from 'app/entities/hestia/code-hint-model'; -import { CodeHintService } from 'app/exercises/shared/exercise-hint/services/code-hint.service'; -import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; -import { MockProfileService } from '../../../helpers/mocks/service/mock-profile.service'; -import { IrisSettingsService } from 'app/iris/settings/shared/iris-settings.service'; -import { IrisSettings } from 'app/entities/iris/settings/iris-settings.model'; -import { ProfileInfo } from 'app/shared/layouts/profiles/profile-info.model'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { PROFILE_IRIS } from 'app/app.constants'; -import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; - -describe('ExerciseHint Management Update Component', () => { - let comp: ExerciseHintUpdateComponent; - let fixture: ComponentFixture; - let service: ExerciseHintService; - let codeHintService: CodeHintService; - let programmingExerciseService: ProgrammingExerciseService; - - const task1 = new ProgrammingExerciseServerSideTask(); - task1.id = 1; - task1.taskName = 'Task 1'; - task1.testCases = [new ProgrammingExerciseTestCase(), new ProgrammingExerciseTestCase()]; - - const task2 = new ProgrammingExerciseServerSideTask(); - task2.id = 2; - task2.taskName = 'Task 2'; - task2.testCases = [new ProgrammingExerciseTestCase(), new ProgrammingExerciseTestCase()]; - - const programmingExercise = new ProgrammingExercise(undefined, undefined); - programmingExercise.id = 15; - - const exerciseHint = new ExerciseHint(); - const route = { data: of({ exerciseHint, exercise: programmingExercise }), params: of({ courseId: 12 }) } as any as ActivatedRoute; - - beforeEach(fakeAsync(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, FormsModule], - declarations: [ExerciseHintUpdateComponent, MockComponent(MarkdownEditorMonacoComponent), MockComponent(HelpIconComponent)], - providers: [ - FormBuilder, - MockProvider(ProgrammingExerciseService), - MockProvider(ExerciseHintService), - MockProvider(TranslateService), - MockProvider(IrisSettingsService), - MockProfileService, - { provide: ActivatedRoute, useValue: route }, - ], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(ExerciseHintUpdateComponent); - comp = fixture.componentInstance; - - service = TestBed.inject(ExerciseHintService); - codeHintService = TestBed.inject(CodeHintService); - programmingExerciseService = TestBed.inject(ProgrammingExerciseService); - flush(); - }); - })); - - afterEach(() => { - exerciseHint.programmingExerciseTask = undefined; - jest.restoreAllMocks(); - }); - - it('should load params and data onInit', () => { - const exercise = new ProgrammingExercise(undefined, undefined); - exercise.programmingLanguage = ProgrammingLanguage.JAVA; - exercise.id = 15; - - comp.ngOnInit(); - - expect(comp.exercise?.id).toBe(15); - expect(comp.exerciseHint).toEqual(exerciseHint); - expect(comp.courseId).toBe(12); - }); - - it('should load and set tasks for exercise hint', fakeAsync(() => { - exerciseHint.id = 4; - comp.exerciseHint = exerciseHint; - const getTasksSpy = jest.spyOn(programmingExerciseService, 'getTasksAndTestsExtractedFromProblemStatement').mockReturnValue(of([task1, task2])); - comp.exerciseHint.programmingExerciseTask = task2; - - comp.ngOnInit(); - - expect(getTasksSpy).toHaveBeenCalledOnce(); - expect(getTasksSpy).toHaveBeenCalledWith(15); - tick(); - expect(comp.tasks).toEqual([task1, task2]); - expect(comp.exerciseHint.programmingExerciseTask).toEqual(task2); - })); - - it('should load and set first tasks to exercise hint', fakeAsync(() => { - comp.exerciseHint = exerciseHint; - const getTasksSpy = jest.spyOn(programmingExerciseService, 'getTasksAndTestsExtractedFromProblemStatement').mockReturnValue(of([task1, task2])); - - comp.ngOnInit(); - - expect(getTasksSpy).toHaveBeenCalledOnce(); - expect(getTasksSpy).toHaveBeenCalledWith(15); - tick(); - expect(comp.tasks).toEqual([task1, task2]); - expect(comp.exerciseHint.programmingExerciseTask).toEqual(task1); - })); - - it('should update description and content using iris', fakeAsync(() => { - // GIVEN - const codeHint1 = new CodeHint(); - codeHint1.id = 123; - codeHint1.programmingExerciseTask = task2; - codeHint1.solutionEntries = [new ProgrammingExerciseSolutionEntry()]; - const codeHint2 = new CodeHint(); - codeHint2.id = 123; - codeHint2.programmingExerciseTask = task2; - codeHint2.content = 'Hello Content'; - codeHint2.description = 'Hello Description'; - - jest.spyOn(codeHintService, 'generateDescriptionForCodeHint').mockReturnValue(of(new HttpResponse({ body: codeHint2 }))); - comp.exerciseHint = codeHint1; - comp.courseId = 1; - comp.exercise = programmingExercise; - - // WHEN - comp.generateDescriptionForCodeHint(); - tick(); - - // THEN - expect(codeHintService.generateDescriptionForCodeHint).toHaveBeenCalledWith(15, 123); - expect(comp.isGeneratingDescription).toBeFalse(); - expect(comp.exerciseHint.content).toBe('Hello Content'); - expect(comp.exerciseHint.description).toBe('Hello Description'); - })); - - it.each<[string[]]>([[[]], [[PROFILE_IRIS]]])( - 'should load iris settings only if profile iris is active', - fakeAsync((activeProfiles: string[]) => { - // Mock getProfileInfo to return activeProfiles - const profileService = TestBed.inject(ProfileService); - jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of({ activeProfiles } as any as ProfileInfo)); - - // Mock getTasksAndTestsExtractedFromProblemStatement - jest.spyOn(programmingExerciseService, 'getTasksAndTestsExtractedFromProblemStatement').mockReturnValue(of([])); - - const fakeSettings = {} as any as IrisSettings; - - // Mock getCombinedProgrammingExerciseSettings - const irisSettingsService = TestBed.inject(IrisSettingsService); - const getCombinedProgrammingExerciseSettingsSpy = jest.spyOn(irisSettingsService, 'getCombinedExerciseSettings').mockReturnValue(of(fakeSettings)); - - // Run ngOnInit - comp.ngOnInit(); - tick(); - - if (activeProfiles.includes(PROFILE_IRIS)) { - // Should have called getCombinedProgrammingExerciseSettings if 'iris' is active - expect(getCombinedProgrammingExerciseSettingsSpy).toHaveBeenCalledOnce(); - expect(comp.irisSettings).toBe(fakeSettings); - } else { - // Should not have called getCombinedProgrammingExerciseSettings if 'iris' is not active - expect(getCombinedProgrammingExerciseSettingsSpy).not.toHaveBeenCalled(); - expect(comp.irisSettings).toBeUndefined(); - } - }), - ); - - it('should generate descriptions', () => { - const codeHint = new CodeHint(); - codeHint.id = 123; - codeHint.programmingExerciseTask = task2; - codeHint.solutionEntries = [new ProgrammingExerciseSolutionEntry()]; - - comp.exerciseHint = codeHint; - comp.courseId = 1; - comp.exercise = programmingExercise; - - const generateDescSpy = jest.spyOn(codeHintService, 'generateDescriptionForCodeHint'); - - comp.generateDescriptionForCodeHint(); - - expect(generateDescSpy).toHaveBeenCalledOnce(); - expect(generateDescSpy).toHaveBeenCalledWith(15, 123); - }); - - describe('save', () => { - it('should call update service on save for existing entity', fakeAsync(() => { - // GIVEN - const entity = new ExerciseHint(); - entity.id = 123; - entity.programmingExerciseTask = task2; - jest.spyOn(service, 'update').mockReturnValue(of(new HttpResponse({ body: entity }))); - comp.exerciseHint = entity; - comp.courseId = 1; - comp.exercise = programmingExercise; - - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.update).toHaveBeenCalledWith(15, entity); - expect(comp.isSaving).toBeFalse(); - })); - - it('should call create service on save for new entity', fakeAsync(() => { - // GIVEN - const entity = new ExerciseHint(); - entity.programmingExerciseTask = task2; - jest.spyOn(service, 'create').mockReturnValue(of(new HttpResponse({ body: entity }))); - comp.exerciseHint = entity; - comp.courseId = 1; - comp.exercise = programmingExercise; - - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.create).toHaveBeenCalledWith(15, entity); - expect(comp.isSaving).toBeFalse(); - })); - }); -}); diff --git a/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint.component.spec.ts b/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint.component.spec.ts deleted file mode 100644 index b7b531efa5a8..000000000000 --- a/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint.component.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { of } from 'rxjs'; -import { HttpHeaders, HttpResponse } from '@angular/common/http'; -import { ActivatedRoute } from '@angular/router'; - -import { ExerciseHintComponent } from 'app/exercises/shared/exercise-hint/manage/exercise-hint.component'; -import { ArtemisTestModule } from '../../../test.module'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; -import { ExerciseHintService } from 'app/exercises/shared/exercise-hint/shared/exercise-hint.service'; -import { MockActivatedRoute } from '../../../helpers/mocks/activated-route/mock-activated-route'; -import { EventManager } from 'app/core/util/event-manager.service'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; - -describe('ExerciseHint Management Component', () => { - let comp: ExerciseHintComponent; - let fixture: ComponentFixture; - let service: ExerciseHintService; - let eventManager: EventManager; - - const programmingExercise = new ProgrammingExercise(undefined, undefined); - programmingExercise.id = 15; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - declarations: [ExerciseHintComponent], - providers: [{ provide: ActivatedRoute, useValue: new MockActivatedRoute({ exerciseId: 15, exercise: programmingExercise }) }], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(ExerciseHintComponent); - comp = fixture.componentInstance; - service = TestBed.inject(ExerciseHintService); - eventManager = TestBed.inject(EventManager); - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should call load all on init with exerciseId from route', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - const hint = new ExerciseHint(); - hint.id = 123; - - jest.spyOn(service, 'findByExerciseId').mockReturnValue( - of( - new HttpResponse({ - body: [hint], - headers, - }), - ), - ); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.findByExerciseId).toHaveBeenCalledOnce(); - expect(service.findByExerciseId).toHaveBeenCalledWith(15); - expect(comp.exerciseHints[0]).toEqual(expect.objectContaining({ id: 123 })); - }); - - it('should invoke hint deletion', () => { - const deleteHintMock = jest.spyOn(service, 'delete').mockReturnValue(of({} as HttpResponse)); - const broadcastSpy = jest.spyOn(eventManager, 'broadcast'); - const exerciseHint = new ExerciseHint(); - exerciseHint.id = 123; - comp.exerciseHints = [exerciseHint]; - comp.exercise = programmingExercise; - - comp.deleteExerciseHint(123); - - expect(deleteHintMock).toHaveBeenCalledOnce(); - expect(deleteHintMock).toHaveBeenCalledWith(15, 123); - expect(broadcastSpy).toHaveBeenCalledOnce(); - expect(broadcastSpy).toHaveBeenCalledWith({ - name: 'exerciseHintListModification', - content: 'Deleted an exerciseHint', - }); - }); - - it('should track item id', () => { - const exerciseHint = new ExerciseHint(); - exerciseHint.id = 1; - expect(comp.trackId(0, exerciseHint)).toBe(1); - }); -}); diff --git a/src/test/javascript/spec/component/exercise-hint/participate/exercise-hint-button-overlay.component.spec.ts b/src/test/javascript/spec/component/exercise-hint/participate/exercise-hint-button-overlay.component.spec.ts deleted file mode 100644 index 005b1c67b1a6..000000000000 --- a/src/test/javascript/spec/component/exercise-hint/participate/exercise-hint-button-overlay.component.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; - -import { ArtemisTestModule } from '../../../test.module'; -import { ExerciseHintButtonOverlayComponent } from 'app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; -import { MockNgbModalService } from '../../../helpers/mocks/service/mock-ngb-modal.service'; -import { ExerciseHintStudentDialogComponent } from 'app/exercises/shared/exercise-hint/participate/exercise-hint-student-dialog.component'; - -describe('Exercise Hint Button Overlay Component', () => { - let comp: ExerciseHintButtonOverlayComponent; - let fixture: ComponentFixture; - - let modalService: NgbModal; - - const availableExerciseHint = new ExerciseHint(); - availableExerciseHint.id = 1; - const activatedExerciseHint = new ExerciseHint(); - activatedExerciseHint.id = 2; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - providers: [{ provide: NgbModal, useClass: MockNgbModalService }], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(ExerciseHintButtonOverlayComponent); - comp = fixture.componentInstance; - - modalService = TestBed.inject(NgbModal); - - comp.availableExerciseHints = [availableExerciseHint]; - comp.activatedExerciseHints = [activatedExerciseHint]; - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should open modal with exercise hints', () => { - const componentInstance = { availableExerciseHints: [], activatedExerciseHints: [] }; - const result = new Promise((resolve) => resolve(true)); - const openModalSpy = jest.spyOn(modalService, 'open').mockReturnValue({ - componentInstance, - result, - }); - - comp.openModal(); - - expect(openModalSpy).toHaveBeenCalledOnce(); - expect(openModalSpy).toHaveBeenCalledWith(ExerciseHintStudentDialogComponent, { size: 'lg', backdrop: 'static' }); - expect(componentInstance.availableExerciseHints).toEqual([availableExerciseHint]); - expect(componentInstance.activatedExerciseHints).toEqual([activatedExerciseHint]); - }); -}); diff --git a/src/test/javascript/spec/component/exercise-hint/participate/exercise-hint-expandable.component.spec.ts b/src/test/javascript/spec/component/exercise-hint/participate/exercise-hint-expandable.component.spec.ts deleted file mode 100644 index 0dbdd2699087..000000000000 --- a/src/test/javascript/spec/component/exercise-hint/participate/exercise-hint-expandable.component.spec.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { of } from 'rxjs'; -import { TranslateService } from '@ngx-translate/core'; -import { MockModule, MockPipe } from 'ng-mocks'; -import { HttpResponse } from '@angular/common/http'; -import { MatExpansionModule } from '@angular/material/expansion'; - -import { ArtemisTestModule } from '../../../test.module'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; -import { ExerciseHintResponse, ExerciseHintService } from 'app/exercises/shared/exercise-hint/shared/exercise-hint.service'; -import { ExerciseHintExpandableComponent } from 'app/exercises/shared/exercise-hint/participate/exercise-hint-expandable.component'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { StarRatingComponent } from 'app/exercises/shared/rating/star-rating/star-rating.component'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; -import { CastToCodeHintPipe } from 'app/exercises/shared/exercise-hint/services/code-hint-cast.pipe'; - -describe('Exercise Hint Expandable Component', () => { - let comp: ExerciseHintExpandableComponent; - let fixture: ComponentFixture; - - let service: ExerciseHintService; - - const exercise = new ProgrammingExercise(undefined, undefined); - exercise.id = 1; - const availableExerciseHint = new ExerciseHint(); - availableExerciseHint.id = 2; - availableExerciseHint.exercise = exercise; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, MockModule(MatExpansionModule)], - declarations: [ExerciseHintExpandableComponent, MockPipe(ArtemisTranslatePipe), StarRatingComponent, CastToCodeHintPipe], - providers: [{ provide: TranslateService, useClass: MockTranslateService }], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(ExerciseHintExpandableComponent); - comp = fixture.componentInstance; - - service = TestBed.inject(ExerciseHintService); - comp.exerciseHint = availableExerciseHint; - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should load available exercise hint content', () => { - const activateHintSpy = jest.spyOn(service, 'activateExerciseHint').mockReturnValue(of({ body: availableExerciseHint } as ExerciseHintResponse)); - comp.displayHintContent(); - expect(activateHintSpy).toHaveBeenCalledOnce(); - expect(activateHintSpy).toHaveBeenCalledWith(1, 2); - expect(comp.expanded).toBeTrue(); - expect(comp.isLoading).toBeFalse(); - expect(comp.hasUsed).toBeTrue(); - expect(comp.exerciseHint).toEqual(availableExerciseHint); - }); - - it('should not load content when hint has already been used', () => { - comp.hasUsed = true; - const activateHintSpy = jest.spyOn(service, 'activateExerciseHint').mockReturnValue(of({ body: availableExerciseHint } as ExerciseHintResponse)); - comp.displayHintContent(); - expect(activateHintSpy).not.toHaveBeenCalled(); - }); - - it('should collapse exercise hint content', () => { - comp.collapseContent(); - expect(comp.expanded).toBeFalse(); - }); - - it('should rate exercise hint', () => { - const rateSpy = jest.spyOn(service, 'rateExerciseHint').mockReturnValue(of({} as HttpResponse)); - comp.exerciseHint = availableExerciseHint; - comp.onRate({ oldValue: 0, newValue: 4, starRating: new StarRatingComponent() }); - - expect(rateSpy).toHaveBeenCalledOnce(); - expect(rateSpy).toHaveBeenCalledWith(1, 2, 4); - expect(comp.exerciseHint.currentUserRating).toBe(4); - }); -}); diff --git a/src/test/javascript/spec/component/exercise-hint/shared/code-hint-container.component.spec.ts b/src/test/javascript/spec/component/exercise-hint/shared/code-hint-container.component.spec.ts deleted file mode 100644 index d7852f272f21..000000000000 --- a/src/test/javascript/spec/component/exercise-hint/shared/code-hint-container.component.spec.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTestModule } from '../../../test.module'; -import { CodeHintContainerComponent } from 'app/exercises/shared/exercise-hint/shared/code-hint-container.component'; -import { CodeHint } from 'app/entities/hestia/code-hint-model'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { CodeHintService } from 'app/exercises/shared/exercise-hint/services/code-hint.service'; - -describe('ExerciseHint Management Component', () => { - let comp: CodeHintContainerComponent; - let fixture: ComponentFixture; - - let service: CodeHintService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - declarations: [CodeHintContainerComponent], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(CodeHintContainerComponent); - comp = fixture.componentInstance; - - service = TestBed.inject(CodeHintService); - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should sort solution entries by file name', () => { - const solutionEntry1 = new ProgrammingExerciseSolutionEntry(); - solutionEntry1.line = 1; - solutionEntry1.filePath = 'b'; - const solutionEntry2 = new ProgrammingExerciseSolutionEntry(); - solutionEntry2.line = 1; - solutionEntry2.filePath = 'a'; - const codeHint = new CodeHint(); - codeHint.solutionEntries = [solutionEntry1, solutionEntry2]; - - comp.codeHint = codeHint; - - comp.ngOnInit(); - expect(comp.sortedSolutionEntries).toEqual([solutionEntry2, solutionEntry1]); - }); - - it('should sort solution entries for same file name but different line', () => { - const solutionEntry1 = new ProgrammingExerciseSolutionEntry(); - solutionEntry1.line = 3; - solutionEntry1.filePath = 'a'; - const solutionEntry2 = new ProgrammingExerciseSolutionEntry(); - solutionEntry2.line = 2; - solutionEntry2.filePath = 'a'; - const codeHint = new CodeHint(); - codeHint.solutionEntries = [solutionEntry1, solutionEntry2]; - - comp.codeHint = codeHint; - - comp.ngOnInit(); - expect(comp.sortedSolutionEntries).toEqual([solutionEntry2, solutionEntry1]); - }); - - it('should remove solution entry', () => { - const solutionEntry1 = new ProgrammingExerciseSolutionEntry(); - solutionEntry1.line = 3; - solutionEntry1.filePath = 'a'; - solutionEntry1.id = 1; - const solutionEntry2 = new ProgrammingExerciseSolutionEntry(); - solutionEntry2.line = 2; - solutionEntry2.filePath = 'a'; - - const exercise = new ProgrammingExercise(undefined, undefined); - exercise.id = 2; - - const codeHint = new CodeHint(); - codeHint.solutionEntries = [solutionEntry1, solutionEntry2]; - codeHint.exercise = exercise; - codeHint.id = 3; - - comp.codeHint = codeHint; - - const removeEntrySpy = jest.spyOn(service, 'removeSolutionEntryFromCodeHint'); - - comp.removeEntryFromCodeHint(solutionEntry1.id); - - expect(removeEntrySpy).toHaveBeenCalledOnce(); - expect(removeEntrySpy).toHaveBeenCalledWith(2, 3, 1); - }); -}); diff --git a/src/test/javascript/spec/component/exercise-hint/shared/solution-entry.component.spec.ts b/src/test/javascript/spec/component/exercise-hint/shared/solution-entry.component.spec.ts deleted file mode 100644 index 4b6bc884a62e..000000000000 --- a/src/test/javascript/spec/component/exercise-hint/shared/solution-entry.component.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { ArtemisTestModule } from '../../../test.module'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { MockComponent, MockPipe } from 'ng-mocks'; -import { SolutionEntryComponent } from 'app/exercises/shared/exercise-hint/shared/solution-entry.component'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { MonacoEditorComponent } from 'app/shared/monaco-editor/monaco-editor.component'; - -describe('Solution Entry Component', () => { - let comp: SolutionEntryComponent; - let fixture: ComponentFixture; - let solutionEntry: ProgrammingExerciseSolutionEntry; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - declarations: [SolutionEntryComponent, MockComponent(MonacoEditorComponent), MockPipe(ArtemisTranslatePipe)], - }).compileComponents(); - fixture = TestBed.createComponent(SolutionEntryComponent); - comp = fixture.componentInstance; - - comp.solutionEntry = new ProgrammingExerciseSolutionEntry(); - comp.solutionEntry.id = 123; - comp.solutionEntry.filePath = '/src/de/Test.java'; - comp.solutionEntry.line = 1; - comp.solutionEntry.code = 'ABC'; - - solutionEntry = comp.solutionEntry; - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should correctly setup editors', () => { - const setStartLineNumberStub = jest.spyOn(comp.editor, 'setStartLineNumber').mockImplementation(); - const changeModelStub = jest.spyOn(comp.editor, 'changeModel').mockImplementation(); - fixture.detectChanges(); - - expect(setStartLineNumberStub).toHaveBeenCalledExactlyOnceWith(solutionEntry.line); - expect(changeModelStub).toHaveBeenCalledExactlyOnceWith(solutionEntry.filePath, solutionEntry.code); - }); - - it('should update the editor height', () => { - const contentHeight = 123; - comp.onContentSizeChange(contentHeight); - expect(comp.editorHeight).toEqual(contentHeight); - }); - - it('should update the solution entry code', () => { - const newCode = 'DEF'; - comp.onEditorContentChange(newCode); - expect(comp.solutionEntry.code).toEqual(newCode); - }); -}); diff --git a/src/test/javascript/spec/component/hestia/git-diff-report/git-diff-file-panel-title.component.spec.ts b/src/test/javascript/spec/component/git-diff-report/git-diff-file-panel-title.component.spec.ts similarity index 89% rename from src/test/javascript/spec/component/hestia/git-diff-report/git-diff-file-panel-title.component.spec.ts rename to src/test/javascript/spec/component/git-diff-report/git-diff-file-panel-title.component.spec.ts index 86380ebed0a7..292f65f903cd 100644 --- a/src/test/javascript/spec/component/hestia/git-diff-report/git-diff-file-panel-title.component.spec.ts +++ b/src/test/javascript/spec/component/git-diff-report/git-diff-file-panel-title.component.spec.ts @@ -1,6 +1,6 @@ -import { ArtemisTestModule } from '../../../test.module'; +import { ArtemisTestModule } from '../../test.module'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { GitDiffFilePanelTitleComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-file-panel-title.component'; +import { GitDiffFilePanelTitleComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel-title.component'; describe('GitDiffFilePanelTitleComponent', () => { let comp: GitDiffFilePanelTitleComponent; diff --git a/src/test/javascript/spec/component/hestia/git-diff-report/git-diff-file-panel.component.spec.ts b/src/test/javascript/spec/component/git-diff-report/git-diff-file-panel.component.spec.ts similarity index 85% rename from src/test/javascript/spec/component/hestia/git-diff-report/git-diff-file-panel.component.spec.ts rename to src/test/javascript/spec/component/git-diff-report/git-diff-file-panel.component.spec.ts index 26627dba3e8f..e62d3af1f108 100644 --- a/src/test/javascript/spec/component/hestia/git-diff-report/git-diff-file-panel.component.spec.ts +++ b/src/test/javascript/spec/component/git-diff-report/git-diff-file-panel.component.spec.ts @@ -1,13 +1,13 @@ -import { ArtemisTestModule } from '../../../test.module'; +import { ArtemisTestModule } from '../../test.module'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MockComponent, MockDirective } from 'ng-mocks'; -import { GitDiffLineStatComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component'; -import { ProgrammingExerciseGitDiffEntry } from 'app/entities/hestia/programming-exercise-git-diff-entry.model'; -import { GitDiffFilePanelComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-file-panel.component'; +import { GitDiffLineStatComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component'; +import { ProgrammingExerciseGitDiffEntry } from '../../../../../main/webapp/app/entities/programming-exercise-git-diff-entry.model'; +import { GitDiffFilePanelComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component'; import { NgbAccordionBody, NgbAccordionButton, NgbAccordionCollapse, NgbAccordionDirective, NgbAccordionHeader, NgbAccordionItem } from '@ng-bootstrap/ng-bootstrap'; -import { GitDiffFileComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-file.component'; -import { GitDiffFilePanelTitleComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-file-panel-title.component'; -import { MonacoDiffEditorComponent } from '../../../../../../main/webapp/app/shared/monaco-editor/monaco-diff-editor.component'; +import { GitDiffFileComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-file.component'; +import { GitDiffFilePanelTitleComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel-title.component'; +import { MonacoDiffEditorComponent } from '../../../../../main/webapp/app/shared/monaco-editor/monaco-diff-editor.component'; describe('GitDiffFilePanelComponent', () => { let comp: GitDiffFilePanelComponent; diff --git a/src/test/javascript/spec/component/hestia/git-diff-report/git-diff-file.component.spec.ts b/src/test/javascript/spec/component/git-diff-report/git-diff-file.component.spec.ts similarity index 83% rename from src/test/javascript/spec/component/hestia/git-diff-report/git-diff-file.component.spec.ts rename to src/test/javascript/spec/component/git-diff-report/git-diff-file.component.spec.ts index 17007596e8a8..2eef960038b3 100644 --- a/src/test/javascript/spec/component/hestia/git-diff-report/git-diff-file.component.spec.ts +++ b/src/test/javascript/spec/component/git-diff-report/git-diff-file.component.spec.ts @@ -1,8 +1,8 @@ -import { ArtemisTestModule } from '../../../test.module'; +import { ArtemisTestModule } from '../../test.module'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { GitDiffFileComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-file.component'; -import { MockResizeObserver } from '../../../helpers/mocks/service/mock-resize-observer'; -import { MonacoDiffEditorComponent } from '../../../../../../main/webapp/app/shared/monaco-editor/monaco-diff-editor.component'; +import { GitDiffFileComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-file.component'; +import { MockResizeObserver } from '../../helpers/mocks/service/mock-resize-observer'; +import { MonacoDiffEditorComponent } from '../../../../../main/webapp/app/shared/monaco-editor/monaco-diff-editor.component'; function getDiffEntryWithPaths(previousFilePath?: string, filePath?: string) { return { diff --git a/src/test/javascript/spec/component/hestia/git-diff-report/git-diff-line-stat.component.spec.ts b/src/test/javascript/spec/component/git-diff-report/git-diff-line-stat.component.spec.ts similarity index 81% rename from src/test/javascript/spec/component/hestia/git-diff-report/git-diff-line-stat.component.spec.ts rename to src/test/javascript/spec/component/git-diff-report/git-diff-line-stat.component.spec.ts index 2ea540371804..dafced43bada 100644 --- a/src/test/javascript/spec/component/hestia/git-diff-report/git-diff-line-stat.component.spec.ts +++ b/src/test/javascript/spec/component/git-diff-report/git-diff-line-stat.component.spec.ts @@ -1,7 +1,7 @@ -import { ArtemisTestModule } from '../../../test.module'; +import { ArtemisTestModule } from '../../test.module'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { GitDiffLineStatComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { GitDiffLineStatComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component'; +import { ArtemisTranslatePipe } from '../../../../../main/webapp/app/shared/pipes/artemis-translate.pipe'; import { MockPipe } from 'ng-mocks'; describe('GitDiffLineStatComponent', () => { diff --git a/src/test/javascript/spec/component/hestia/git-diff-report/git-diff-modal.component.spec.ts b/src/test/javascript/spec/component/git-diff-report/git-diff-modal.component.spec.ts similarity index 91% rename from src/test/javascript/spec/component/hestia/git-diff-report/git-diff-modal.component.spec.ts rename to src/test/javascript/spec/component/git-diff-report/git-diff-modal.component.spec.ts index 7fc9a8693d0e..e2da096b0e41 100644 --- a/src/test/javascript/spec/component/hestia/git-diff-report/git-diff-modal.component.spec.ts +++ b/src/test/javascript/spec/component/git-diff-report/git-diff-modal.component.spec.ts @@ -1,13 +1,13 @@ -import { GitDiffReportComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-report.component'; -import { ArtemisTestModule } from '../../../test.module'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { GitDiffReportComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-report.component'; +import { ArtemisTestModule } from '../../test.module'; +import { ArtemisTranslatePipe } from '../../../../../main/webapp/app/shared/pipes/artemis-translate.pipe'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { GitDiffReportModalComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-report-modal.component'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; +import { GitDiffReportModalComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-report-modal.component'; +import { ProgrammingExerciseService } from '../../../../../main/webapp/app/exercises/programming/manage/services/programming-exercise.service'; import { of, throwError } from 'rxjs'; -import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; +import { ProgrammingExerciseParticipationService } from '../../../../../main/webapp/app/exercises/programming/manage/services/programming-exercise-participation.service'; import { MockComponent, MockPipe } from 'ng-mocks'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffReport } from '../../../../../main/webapp/app/entities/programming-exercise-git-diff-report.model'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; describe('GitDiffReportModalComponent', () => { diff --git a/src/test/javascript/spec/component/hestia/git-diff-report/git-diff-report.component.spec.ts b/src/test/javascript/spec/component/git-diff-report/git-diff-report.component.spec.ts similarity index 90% rename from src/test/javascript/spec/component/hestia/git-diff-report/git-diff-report.component.spec.ts rename to src/test/javascript/spec/component/git-diff-report/git-diff-report.component.spec.ts index 143c150edc57..8e8a531d3a1b 100644 --- a/src/test/javascript/spec/component/hestia/git-diff-report/git-diff-report.component.spec.ts +++ b/src/test/javascript/spec/component/git-diff-report/git-diff-report.component.spec.ts @@ -1,13 +1,13 @@ -import { ArtemisTestModule } from '../../../test.module'; +import { ArtemisTestModule } from '../../test.module'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisTranslatePipe } from '../../../../../main/webapp/app/shared/pipes/artemis-translate.pipe'; import { MockComponent, MockPipe } from 'ng-mocks'; -import { GitDiffLineStatComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-line-stat.component'; -import { GitDiffReportComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-report.component'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; -import { ProgrammingExerciseGitDiffEntry } from 'app/entities/hestia/programming-exercise-git-diff-entry.model'; -import { NgbTooltipMocksModule } from '../../../helpers/mocks/directive/ngbTooltipMocks.module'; -import { GitDiffFilePanelComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-file-panel.component'; +import { GitDiffLineStatComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component'; +import { GitDiffReportComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-report.component'; +import { ProgrammingExerciseGitDiffReport } from '../../../../../main/webapp/app/entities/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffEntry } from '../../../../../main/webapp/app/entities/programming-exercise-git-diff-entry.model'; +import { NgbTooltipMocksModule } from '../../helpers/mocks/directive/ngbTooltipMocks.module'; +import { GitDiffFilePanelComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component'; describe('ProgrammingExerciseGitDiffReport Component', () => { let comp: GitDiffReportComponent; diff --git a/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-overview.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-overview.component.spec.ts deleted file mode 100644 index 360a08238306..000000000000 --- a/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-overview.component.spec.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { ArtemisTestModule } from '../../../test.module'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { CodeHint, CodeHintGenerationStep } from 'app/entities/hestia/code-hint-model'; -import { CodeHintGenerationOverviewComponent } from 'app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component'; -import { MockActivatedRoute } from '../../../helpers/mocks/activated-route/mock-activated-route'; -import { ActivatedRoute } from '@angular/router'; -import { CoverageReport } from 'app/entities/hestia/coverage-report.model'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; - -describe('CodeHintGenerationOverview Component', () => { - let comp: CodeHintGenerationOverviewComponent; - let fixture: ComponentFixture; - - const exercise = new ProgrammingExercise(undefined, undefined); - exercise.id = 1; - exercise.buildConfig!.testwiseCoverageEnabled = true; - - let latestStepSpy: jest.SpyInstance; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - declarations: [CodeHintGenerationOverviewComponent], - providers: [{ provide: ActivatedRoute, useValue: new MockActivatedRoute({ exercise }) }], - }).compileComponents(); - fixture = TestBed.createComponent(CodeHintGenerationOverviewComponent); - comp = fixture.componentInstance; - - latestStepSpy = jest.spyOn(comp, 'setLatestPerformedStep'); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should set all step status on init', () => { - comp.ngOnInit(); - - const expectedStatus = new Map(); - expectedStatus.set(CodeHintGenerationStep.GIT_DIFF, false); - expectedStatus.set(CodeHintGenerationStep.COVERAGE, false); - expectedStatus.set(CodeHintGenerationStep.SOLUTION_ENTRIES, false); - expectedStatus.set(CodeHintGenerationStep.CODE_HINTS, false); - - expect(comp.isPerformedByStep).toEqual(expectedStatus); - expect(comp.allowBehavioralEntryGeneration).toBeTrue(); - expect(comp.currentStep).toEqual(CodeHintGenerationStep.GIT_DIFF); - }); - - it('should set next step as current step', () => { - comp.currentStep = CodeHintGenerationStep.GIT_DIFF; - comp.onNextStep(); - expect(comp.currentStep).toBe(CodeHintGenerationStep.COVERAGE); - }); - - it('should set previous step as current step', () => { - comp.currentStep = CodeHintGenerationStep.COVERAGE; - comp.onPreviousStep(); - expect(comp.currentStep).toBe(CodeHintGenerationStep.GIT_DIFF); - }); - - it('should check if next step is available', () => { - const expectedStatus = new Map(); - comp.isPerformedByStep = expectedStatus; - comp.currentStep = CodeHintGenerationStep.GIT_DIFF; - expectedStatus.set(CodeHintGenerationStep.GIT_DIFF, false); - - let isAvailable = comp.isNextStepAvailable(); - expect(isAvailable).toBeFalse(); - - expectedStatus.set(CodeHintGenerationStep.GIT_DIFF, true); - - isAvailable = comp.isNextStepAvailable(); - expect(isAvailable).toBeTrue(); - }); - - it('should set specific step as current step', () => { - comp.currentStep = CodeHintGenerationStep.GIT_DIFF; - comp.onStepChange(CodeHintGenerationStep.COVERAGE); - expect(comp.currentStep).toBe(CodeHintGenerationStep.COVERAGE); - }); - - it('should update diff step status', () => { - latestStepSpy.mockReturnValue(undefined); - comp.isPerformedByStep = new Map(); - comp.onDiffReportLoaded(undefined); - - expect(latestStepSpy).toHaveBeenCalledOnce(); - expect(latestStepSpy).toHaveBeenCalledWith(CodeHintGenerationStep.GIT_DIFF); - expect(comp.isPerformedByStep.get(CodeHintGenerationStep.GIT_DIFF)).toBeFalse(); - - comp.onDiffReportLoaded(new ProgrammingExerciseGitDiffReport()); - expect(latestStepSpy).toHaveBeenCalledTimes(2); - expect(latestStepSpy).toHaveBeenNthCalledWith(2, CodeHintGenerationStep.GIT_DIFF); - expect(comp.isPerformedByStep.get(CodeHintGenerationStep.GIT_DIFF)).toBeTrue(); - }); - - it('should update coverage step status', () => { - latestStepSpy.mockReturnValue(undefined); - comp.isPerformedByStep = new Map(); - comp.onCoverageReportLoaded(undefined); - - expect(latestStepSpy).toHaveBeenCalledOnce(); - expect(latestStepSpy).toHaveBeenCalledWith(CodeHintGenerationStep.COVERAGE); - expect(comp.isPerformedByStep.get(CodeHintGenerationStep.COVERAGE)).toBeFalse(); - - comp.onCoverageReportLoaded(new CoverageReport()); - expect(latestStepSpy).toHaveBeenCalledTimes(2); - expect(latestStepSpy).toHaveBeenNthCalledWith(2, CodeHintGenerationStep.COVERAGE); - expect(comp.isPerformedByStep.get(CodeHintGenerationStep.COVERAGE)).toBeTrue(); - }); - - it('should update solution entry step status', () => { - latestStepSpy.mockReturnValue(undefined); - comp.isPerformedByStep = new Map(); - comp.onSolutionEntryChanges(undefined); - - expect(latestStepSpy).toHaveBeenCalledOnce(); - expect(latestStepSpy).toHaveBeenCalledWith(CodeHintGenerationStep.SOLUTION_ENTRIES); - expect(comp.isPerformedByStep.get(CodeHintGenerationStep.SOLUTION_ENTRIES)).toBeFalse(); - - comp.onSolutionEntryChanges([]); - expect(latestStepSpy).toHaveBeenCalledTimes(2); - expect(latestStepSpy).toHaveBeenNthCalledWith(2, CodeHintGenerationStep.SOLUTION_ENTRIES); - expect(comp.isPerformedByStep.get(CodeHintGenerationStep.SOLUTION_ENTRIES)).toBeFalse(); - - comp.onSolutionEntryChanges([new ProgrammingExerciseSolutionEntry()]); - expect(latestStepSpy).toHaveBeenCalledTimes(3); - expect(latestStepSpy).toHaveBeenNthCalledWith(3, CodeHintGenerationStep.SOLUTION_ENTRIES); - expect(comp.isPerformedByStep.get(CodeHintGenerationStep.SOLUTION_ENTRIES)).toBeTrue(); - }); - - it('should update code hint step status', () => { - latestStepSpy.mockReturnValue(undefined); - comp.isPerformedByStep = new Map(); - comp.onCodeHintsLoaded(undefined); - - expect(latestStepSpy).toHaveBeenCalledOnce(); - expect(latestStepSpy).toHaveBeenCalledWith(CodeHintGenerationStep.CODE_HINTS); - expect(comp.isPerformedByStep.get(CodeHintGenerationStep.CODE_HINTS)).toBeFalse(); - - comp.onCodeHintsLoaded([]); - expect(latestStepSpy).toHaveBeenCalledTimes(2); - expect(latestStepSpy).toHaveBeenNthCalledWith(2, CodeHintGenerationStep.CODE_HINTS); - expect(comp.isPerformedByStep.get(CodeHintGenerationStep.CODE_HINTS)).toBeFalse(); - - comp.onCodeHintsLoaded([new CodeHint()]); - expect(latestStepSpy).toHaveBeenCalledTimes(3); - expect(latestStepSpy).toHaveBeenNthCalledWith(3, CodeHintGenerationStep.CODE_HINTS); - expect(comp.isPerformedByStep.get(CodeHintGenerationStep.CODE_HINTS)).toBeTrue(); - }); - - it('should not update latest performed step for update on previous step', () => { - comp.currentStep = CodeHintGenerationStep.SOLUTION_ENTRIES; - comp.isPerformedByStep = new Map(); - comp.isPerformedByStep.set(CodeHintGenerationStep.SOLUTION_ENTRIES, true); - - comp.isPerformedByStep.set(CodeHintGenerationStep.GIT_DIFF, true); - comp.setLatestPerformedStep(CodeHintGenerationStep.GIT_DIFF); - - expect(comp.currentStep).toBe(CodeHintGenerationStep.SOLUTION_ENTRIES); - }); - - it('should select latest performed step for update on later step', () => { - comp.currentStep = CodeHintGenerationStep.GIT_DIFF; - comp.isPerformedByStep = new Map(); - comp.isPerformedByStep.set(CodeHintGenerationStep.GIT_DIFF, true); - - comp.isPerformedByStep.set(CodeHintGenerationStep.SOLUTION_ENTRIES, true); - comp.setLatestPerformedStep(CodeHintGenerationStep.SOLUTION_ENTRIES); - - expect(comp.currentStep).toBe(CodeHintGenerationStep.SOLUTION_ENTRIES); - }); -}); diff --git a/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-status.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-status.component.spec.ts deleted file mode 100644 index 7e0443998f36..000000000000 --- a/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-status.component.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ArtemisTestModule } from '../../../test.module'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { CodeHintGenerationStep } from 'app/entities/hestia/code-hint-model'; -import { CodeHintGenerationStatusComponent } from 'app/exercises/programming/hestia/generation-overview/code-hint-generation-status/code-hint-generation-status.component'; - -describe('CodeHintGenerationStatus Component', () => { - let comp: CodeHintGenerationStatusComponent; - let fixture: ComponentFixture; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - }).compileComponents(); - fixture = TestBed.createComponent(CodeHintGenerationStatusComponent); - comp = fixture.componentInstance; - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should select step', () => { - const stepChangeSpy = jest.spyOn(comp.onStepChange, 'emit'); - comp.onSelectStep(CodeHintGenerationStep.GIT_DIFF); - expect(stepChangeSpy).toHaveBeenCalledOnce(); - expect(stepChangeSpy).toHaveBeenCalledWith(CodeHintGenerationStep.GIT_DIFF); - }); -}); diff --git a/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-step.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-step.component.spec.ts deleted file mode 100644 index f0e9e9999233..000000000000 --- a/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-step.component.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { ArtemisTestModule } from '../../../test.module'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { of } from 'rxjs'; -import { CodeHintGenerationStepComponent } from 'app/exercises/programming/hestia/generation-overview/steps/code-hint-generation-step/code-hint-generation-step.component'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { CodeHintService } from 'app/exercises/shared/exercise-hint/services/code-hint.service'; -import { CodeHint } from 'app/entities/hestia/code-hint-model'; - -describe('CodeHintGenerationStep Component', () => { - let comp: CodeHintGenerationStepComponent; - let fixture: ComponentFixture; - - let exerciseService: ProgrammingExerciseService; - let codeHintService: CodeHintService; - - let onHintsLoadedSpy: jest.SpyInstance; - - let exercise: ProgrammingExercise; - let codeHints: CodeHint[]; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - }).compileComponents(); - fixture = TestBed.createComponent(CodeHintGenerationStepComponent); - comp = fixture.componentInstance; - - exerciseService = TestBed.inject(ProgrammingExerciseService); - codeHintService = TestBed.inject(CodeHintService); - - onHintsLoadedSpy = jest.spyOn(comp.onCodeHintsLoaded, 'emit'); - - exercise = new ProgrammingExercise(undefined, undefined); - exercise.id = 1; - comp.exercise = exercise; - - codeHints = [new CodeHint(), new CodeHint()]; - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should load all code hints on init', () => { - const loadHintsSpy = jest.spyOn(exerciseService, 'getCodeHintsForExercise').mockReturnValue(of(codeHints)); - - comp.ngOnInit(); - - expect(loadHintsSpy).toHaveBeenCalledOnce(); - expect(loadHintsSpy).toHaveBeenCalledWith(1); - expect(comp.codeHints).toEqual(codeHints); - expect(comp.isLoading).toBeFalse(); - expect(onHintsLoadedSpy).toHaveBeenCalledOnce(); - }); - - it('should generate code hints', () => { - const generateHintsSpy = jest.spyOn(codeHintService, 'generateCodeHintsForExercise').mockReturnValue(of(codeHints)); - - comp.generateCodeHints(true, 'recreateHintsButton'); - - expect(generateHintsSpy).toHaveBeenCalledOnce(); - expect(generateHintsSpy).toHaveBeenCalledWith(1, true); - expect(comp.codeHints).toEqual(codeHints); - expect(comp.isLoading).toBeFalse(); - expect(onHintsLoadedSpy).toHaveBeenCalledOnce(); - expect(onHintsLoadedSpy).toHaveBeenCalledWith(codeHints); - }); -}); diff --git a/src/test/javascript/spec/component/hestia/generation-overview/coverage-generation-step.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/coverage-generation-step.component.spec.ts deleted file mode 100644 index cda7ed0c1f60..000000000000 --- a/src/test/javascript/spec/component/hestia/generation-overview/coverage-generation-step.component.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { ArtemisTestModule } from '../../../test.module'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { of } from 'rxjs'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { CoverageGenerationStepComponent } from 'app/exercises/programming/hestia/generation-overview/steps/coverage-generation-step/coverage-generation-step.component'; -import { CoverageReport } from 'app/entities/hestia/coverage-report.model'; - -describe('CoverageGenerationStep Component', () => { - let comp: CoverageGenerationStepComponent; - let fixture: ComponentFixture; - - let exerciseService: ProgrammingExerciseService; - - let onCoverageLoadedSpy: jest.SpyInstance; - - let exercise: ProgrammingExercise; - let fileContentByPath: Map; - let coverageReport: CoverageReport; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - }).compileComponents(); - fixture = TestBed.createComponent(CoverageGenerationStepComponent); - comp = fixture.componentInstance; - - exerciseService = TestBed.inject(ProgrammingExerciseService); - - onCoverageLoadedSpy = jest.spyOn(comp.onCoverageLoaded, 'emit'); - - exercise = new ProgrammingExercise(undefined, undefined); - exercise.id = 1; - comp.exercise = exercise; - - fileContentByPath = new Map(); - fileContentByPath.set('A.java', 'abc'); - fileContentByPath.set('B.java', 'def'); - - coverageReport = new CoverageReport(); - coverageReport.id = 2; - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should load all code hints on init', () => { - const loadFilesSpy = jest.spyOn(exerciseService, 'getSolutionRepositoryTestFilesWithContent').mockReturnValue(of(fileContentByPath)); - const loadReportSpy = jest.spyOn(exerciseService, 'getLatestFullTestwiseCoverageReport').mockReturnValue(of(coverageReport)); - - comp.ngOnInit(); - - expect(loadFilesSpy).toHaveBeenCalledOnce(); - expect(loadFilesSpy).toHaveBeenCalledWith(1); - expect(loadReportSpy).toHaveBeenCalledOnce(); - expect(loadReportSpy).toHaveBeenCalledWith(1); - - expect(comp.fileContentByPath).toEqual(fileContentByPath); - expect(comp.coverageReport).toEqual(coverageReport); - expect(comp.isLoading).toBeFalse(); - expect(onCoverageLoadedSpy).toHaveBeenCalledOnce(); - expect(onCoverageLoadedSpy).toHaveBeenCalledWith(coverageReport); - }); -}); diff --git a/src/test/javascript/spec/component/hestia/generation-overview/diff-generation-step.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/diff-generation-step.component.spec.ts deleted file mode 100644 index 6d9f4cca6346..000000000000 --- a/src/test/javascript/spec/component/hestia/generation-overview/diff-generation-step.component.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { ArtemisTestModule } from '../../../test.module'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { of } from 'rxjs'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { DiffGenerationStepComponent } from 'app/exercises/programming/hestia/generation-overview/steps/diff-generation-step/diff-generation-step.component'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; - -describe('DiffGenerationStep Component', () => { - let comp: DiffGenerationStepComponent; - let fixture: ComponentFixture; - - let exerciseService: ProgrammingExerciseService; - - let onGitDiffLoadedSpy: jest.SpyInstance; - - let exercise: ProgrammingExercise; - let fileContentByPath: Map; - let gitDiffReport: ProgrammingExerciseGitDiffReport; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - }).compileComponents(); - fixture = TestBed.createComponent(DiffGenerationStepComponent); - comp = fixture.componentInstance; - - exerciseService = TestBed.inject(ProgrammingExerciseService); - - onGitDiffLoadedSpy = jest.spyOn(comp.onGitDiffLoaded, 'emit'); - - exercise = new ProgrammingExercise(undefined, undefined); - exercise.id = 1; - comp.exercise = exercise; - - fileContentByPath = new Map(); - fileContentByPath.set('A.java', 'abc'); - fileContentByPath.set('B.java', 'def'); - - gitDiffReport = new ProgrammingExerciseGitDiffReport(); - gitDiffReport.programmingExercise = exercise; - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should load all code hints on init', () => { - const loadTemplateFilesSpy = jest.spyOn(exerciseService, 'getTemplateRepositoryTestFilesWithContent').mockReturnValue(of(fileContentByPath)); - const loadSolutionFilesSpy = jest.spyOn(exerciseService, 'getSolutionRepositoryTestFilesWithContent').mockReturnValue(of(fileContentByPath)); - const loadReportSpy = jest.spyOn(exerciseService, 'getDiffReport').mockReturnValue(of(gitDiffReport)); - - comp.ngOnInit(); - - expect(loadTemplateFilesSpy).toHaveBeenCalledOnce(); - expect(loadTemplateFilesSpy).toHaveBeenCalledWith(1); - expect(loadSolutionFilesSpy).toHaveBeenCalledOnce(); - expect(loadSolutionFilesSpy).toHaveBeenCalledWith(1); - expect(loadReportSpy).toHaveBeenCalledOnce(); - expect(loadReportSpy).toHaveBeenCalledWith(1); - - expect(comp.gitDiffReport).toEqual(gitDiffReport); - expect(comp.isLoading).toBeFalse(); - expect(onGitDiffLoadedSpy).toHaveBeenCalledOnce(); - expect(onGitDiffLoadedSpy).toHaveBeenCalledWith(gitDiffReport); - }); -}); diff --git a/src/test/javascript/spec/component/hestia/generation-overview/manual-solution-entry-creation-modal.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/manual-solution-entry-creation-modal.component.spec.ts deleted file mode 100644 index 9720294dde47..000000000000 --- a/src/test/javascript/spec/component/hestia/generation-overview/manual-solution-entry-creation-modal.component.spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { ArtemisTestModule } from '../../../test.module'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ManualSolutionEntryCreationModalComponent } from 'app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExerciseSolutionEntryService } from 'app/exercises/shared/exercise-hint/services/programming-exercise-solution-entry.service'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; -import { of } from 'rxjs'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { SolutionEntryComponent } from 'app/exercises/shared/exercise-hint/shared/solution-entry.component'; -import { MockComponent } from 'ng-mocks'; - -describe('ManualSolutionEntryCreationModal Component', () => { - let comp: ManualSolutionEntryCreationModalComponent; - let fixture: ComponentFixture; - - let exerciseService: ProgrammingExerciseService; - let solutionEntryService: ProgrammingExerciseSolutionEntryService; - let modalService: NgbActiveModal; - - let testCases: ProgrammingExerciseTestCase[]; - const solutionFiles = ['A.java', 'B.java']; - - let testCaseSpy: jest.SpyInstance; - let getFileNamesSpy: jest.SpyInstance; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - declarations: [ManualSolutionEntryCreationModalComponent, MockComponent(SolutionEntryComponent)], - }).compileComponents(); - fixture = TestBed.createComponent(ManualSolutionEntryCreationModalComponent); - comp = fixture.componentInstance; - - exerciseService = TestBed.inject(ProgrammingExerciseService); - solutionEntryService = TestBed.inject(ProgrammingExerciseSolutionEntryService); - modalService = TestBed.inject(NgbActiveModal); - - const testCase1 = new ProgrammingExerciseTestCase(); - testCase1.id = 2; - const testCase2 = new ProgrammingExerciseTestCase(); - testCase2.id = 3; - testCases = [testCase1, testCase2]; - testCaseSpy = jest.spyOn(exerciseService, 'getAllTestCases').mockReturnValue(of(testCases)); - - getFileNamesSpy = jest.spyOn(exerciseService, 'getSolutionFileNames').mockReturnValue(of(solutionFiles)); - - comp.exerciseId = 1; - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should load test cases and files on init', () => { - comp.ngOnInit(); - - expect(testCaseSpy).toHaveBeenCalledOnce(); - expect(testCaseSpy).toHaveBeenCalledWith(1); - expect(comp.testCases).toEqual(testCases); - expect(getFileNamesSpy).toHaveBeenCalledOnce(); - expect(getFileNamesSpy).toHaveBeenCalledWith(1); - expect(comp.solutionRepositoryFileNames).toEqual(solutionFiles); - }); - - it('should create manual entry', () => { - const entryToCreate = new ProgrammingExerciseSolutionEntry(); - entryToCreate.testCase = testCases[0]; - comp.solutionEntry = entryToCreate; - - const createdEntry = new ProgrammingExerciseSolutionEntry(); - createdEntry.id = 4; - const createSpy = jest.spyOn(solutionEntryService, 'createSolutionEntry').mockReturnValue(of(createdEntry)); - const createdEmitterSpy = jest.spyOn(comp.onEntryCreated, 'emit'); - const closedSpy = jest.spyOn(modalService, 'close'); - - comp.onCreateEntry(); - - expect(createSpy).toHaveBeenCalledOnce(); - expect(createSpy).toHaveBeenCalledWith(1, 2, entryToCreate); - expect(createdEmitterSpy).toHaveBeenCalledOnce(); - expect(createdEmitterSpy).toHaveBeenCalledWith(createdEntry); - expect(closedSpy).toHaveBeenCalledOnce(); - }); - - it('should close modal', () => { - const closedSpy = jest.spyOn(modalService, 'close'); - comp.clear(); - expect(closedSpy).toHaveBeenCalledOnce(); - }); - - it('should setup editor when file path changes', () => { - comp.solutionEntryComponent = { setupEditor: jest.fn() } as unknown as SolutionEntryComponent; - comp.onUpdateFilePath(); - expect(comp.solutionEntryComponent.setupEditor).toHaveBeenCalledOnce(); - }); -}); diff --git a/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-details-modal.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-details-modal.component.spec.ts deleted file mode 100644 index 7852a6aa8f5f..000000000000 --- a/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-details-modal.component.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { ArtemisTestModule } from '../../../test.module'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ProgrammingExerciseSolutionEntryService } from 'app/exercises/shared/exercise-hint/services/programming-exercise-solution-entry.service'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; -import { of } from 'rxjs'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { SolutionEntryDetailsModalComponent } from 'app/exercises/programming/hestia/generation-overview/solution-entry-details-modal/solution-entry-details-modal.component'; - -describe('SolutionEntryDetailsModal Component', () => { - let comp: SolutionEntryDetailsModalComponent; - let fixture: ComponentFixture; - - let solutionEntryService: ProgrammingExerciseSolutionEntryService; - let modalService: NgbActiveModal; - - let solutionEntry: ProgrammingExerciseSolutionEntry; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - }).compileComponents(); - fixture = TestBed.createComponent(SolutionEntryDetailsModalComponent); - comp = fixture.componentInstance; - - solutionEntryService = TestBed.inject(ProgrammingExerciseSolutionEntryService); - modalService = TestBed.inject(NgbActiveModal); - - comp.exerciseId = 1; - - const testCase = new ProgrammingExerciseTestCase(); - testCase.id = 2; - - solutionEntry = new ProgrammingExerciseSolutionEntry(); - solutionEntry.id = 3; - solutionEntry.testCase = testCase; - - comp.solutionEntry = solutionEntry; - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should create manual entry', () => { - const updateSpy = jest.spyOn(solutionEntryService, 'updateSolutionEntry').mockReturnValue(of(solutionEntry)); - const closedSpy = jest.spyOn(modalService, 'close'); - - comp.saveSolutionEntry(); - - expect(updateSpy).toHaveBeenCalledOnce(); - expect(updateSpy).toHaveBeenCalledWith(1, 2, 3, solutionEntry); - expect(closedSpy).toHaveBeenCalledOnce(); - expect(comp.solutionEntry).toEqual(solutionEntry); - }); - - it('should close modal', () => { - const closedSpy = jest.spyOn(modalService, 'close'); - comp.clear(); - expect(closedSpy).toHaveBeenCalledOnce(); - }); -}); diff --git a/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-generation-step.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-generation-step.component.spec.ts deleted file mode 100644 index 9e4ff7118bc9..000000000000 --- a/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-generation-step.component.spec.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { ArtemisTestModule } from '../../../test.module'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Observable, of } from 'rxjs'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { SolutionEntryGenerationStepComponent } from 'app/exercises/programming/hestia/generation-overview/steps/solution-entry-generation-step/solution-entry-generation-step.component'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { AlertService } from 'app/core/util/alert.service'; -import { ProgrammingExerciseSolutionEntryService } from 'app/exercises/shared/exercise-hint/services/programming-exercise-solution-entry.service'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { MockComponent, MockPipe, MockProvider } from 'ng-mocks'; -import { EventEmitter } from '@angular/core'; -import { ProgrammingExerciseTestCase, ProgrammingExerciseTestCaseType } from 'app/entities/programming/programming-exercise-test-case.model'; -import { SortingOrder } from 'app/shared/table/pageable-table'; -import { HelpIconComponent } from 'app/shared/components/help-icon.component'; - -describe('SolutionEntryGenerationStep Component', () => { - let comp: SolutionEntryGenerationStepComponent; - let fixture: ComponentFixture; - - let exerciseService: ProgrammingExerciseService; - let modalService: NgbModal; - let alertService: AlertService; - let solutionEntryService: ProgrammingExerciseSolutionEntryService; - - let onEntryUpdatedSpy: jest.SpyInstance; - - let exercise: ProgrammingExercise; - let entry1: ProgrammingExerciseSolutionEntry; - let entry2: ProgrammingExerciseSolutionEntry; - let solutionEntries: ProgrammingExerciseSolutionEntry[]; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - declarations: [SolutionEntryGenerationStepComponent, MockPipe(ArtemisTranslatePipe), MockComponent(HelpIconComponent)], - providers: [MockProvider(NgbModal), MockProvider(AlertService), MockProvider(ArtemisTranslatePipe)], - }).compileComponents(); - fixture = TestBed.createComponent(SolutionEntryGenerationStepComponent); - comp = fixture.componentInstance; - - exerciseService = TestBed.inject(ProgrammingExerciseService); - modalService = TestBed.inject(NgbModal); - alertService = TestBed.inject(AlertService); - solutionEntryService = TestBed.inject(ProgrammingExerciseSolutionEntryService); - - onEntryUpdatedSpy = jest.spyOn(comp.onEntryUpdate, 'emit'); - - exercise = new ProgrammingExercise(undefined, undefined); - exercise.id = 1; - comp.exercise = exercise; - comp.solutionEntries = []; - - entry1 = new ProgrammingExerciseSolutionEntry(); - entry1.id = 2; - const testCase1 = new ProgrammingExerciseTestCase(); - testCase1.id = 4; - testCase1.type = ProgrammingExerciseTestCaseType.STRUCTURAL; - testCase1.testName = 'test1'; - entry1.testCase = testCase1; - - entry2 = new ProgrammingExerciseSolutionEntry(); - entry2.id = 3; - const testCase2 = new ProgrammingExerciseTestCase(); - testCase2.id = 5; - testCase2.type = ProgrammingExerciseTestCaseType.BEHAVIORAL; - testCase2.testName = 'test2'; - entry2.testCase = testCase2; - - solutionEntries = [entry1, entry2]; - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should load all solution entries on init', () => { - const loadEntriesSpy = jest.spyOn(solutionEntryService, 'getSolutionEntriesForExercise').mockReturnValue(of(solutionEntries)); - - comp.ngOnInit(); - - expect(loadEntriesSpy).toHaveBeenCalledOnce(); - expect(loadEntriesSpy).toHaveBeenCalledWith(1); - expect(comp.solutionEntries).toEqual(solutionEntries); - expect(comp.isLoading).toBeFalse(); - expect(onEntryUpdatedSpy).toHaveBeenCalledOnce(); - expect(onEntryUpdatedSpy).toHaveBeenCalledWith(solutionEntries); - }); - - it('should open editable solution entry details modal', () => { - const mockModal = { - result: Promise.resolve(), - componentInstance: {}, - } as NgbModalRef; - const openSpy = jest.spyOn(modalService, 'open').mockReturnValue(mockModal); - - comp.openSolutionEntryModal(entry1, true); - - expect(openSpy).toHaveBeenCalledOnce(); - expect(mockModal.componentInstance.exerciseId).toBe(1); - expect(mockModal.componentInstance.solutionEntry).toEqual(entry1); - expect(mockModal.componentInstance.isEditable).toBeTrue(); - }); - - it('should open manual solution entry creation modal', () => { - const mockModal = { - result: Promise.resolve(), - componentInstance: {}, - } as NgbModalRef; - const emitter = new EventEmitter(); - mockModal.componentInstance.onEntryCreated = emitter; - - const openSpy = jest.spyOn(modalService, 'open').mockReturnValue(mockModal); - - comp.openManualEntryCreationModal(); - - expect(openSpy).toHaveBeenCalledOnce(); - expect(mockModal.componentInstance.exerciseId).toBe(1); - - emitter.emit(entry1); - expect(comp.solutionEntries).toHaveLength(1); - expect(comp.solutionEntries).toContain(entry1); - }); - - it('should generate structural solution entries', () => { - const generateSpy = jest.spyOn(exerciseService, 'createStructuralSolutionEntries').mockReturnValue(of([entry1])); - const successSpy = jest.spyOn(alertService, 'success'); - // only one behavioral entry - comp.solutionEntries = [entry2]; - - comp.onGenerateStructuralSolutionEntries(); - - expect(generateSpy).toHaveBeenCalledOnce(); - expect(generateSpy).toHaveBeenCalledWith(1); - expect(successSpy).toHaveBeenCalledOnce(); - expect(comp.solutionEntries).toContainAllValues([entry1, entry2]); - expect(comp.testCaseSortOrder).toBeUndefined(); - expect(onEntryUpdatedSpy).toHaveBeenCalledOnce(); - expect(onEntryUpdatedSpy).toHaveBeenCalledWith([entry2, entry1]); - }); - - it('should generate behavioral solution entries', () => { - const generateSpy = jest.spyOn(exerciseService, 'createBehavioralSolutionEntries').mockReturnValue(of([entry2])); - const successSpy = jest.spyOn(alertService, 'success'); - // only one structural entry - comp.solutionEntries = [entry1]; - - comp.onGenerateBehavioralSolutionEntries(); - - expect(generateSpy).toHaveBeenCalledOnce(); - expect(generateSpy).toHaveBeenCalledWith(1); - expect(successSpy).toHaveBeenCalledOnce(); - expect(comp.solutionEntries).toContainAllValues([entry1, entry2]); - expect(comp.testCaseSortOrder).toBeUndefined(); - expect(onEntryUpdatedSpy).toHaveBeenCalledOnce(); - expect(onEntryUpdatedSpy).toHaveBeenCalledWith(solutionEntries); - }); - - it('should delete all solution entries', () => { - const deleteAllSpy = jest.spyOn(solutionEntryService, 'deleteAllSolutionEntriesForExercise').mockReturnValue(new Observable((s) => s.next())); - comp.deleteAllSolutionEntries(); - - expect(deleteAllSpy).toHaveBeenCalledOnce(); - expect(deleteAllSpy).toHaveBeenCalledWith(1); - expect(comp.solutionEntries).toHaveLength(0); - expect(onEntryUpdatedSpy).toHaveBeenCalledOnce(); - expect(onEntryUpdatedSpy).toHaveBeenCalledWith([]); - }); - - it('should delete individual solution entry', () => { - const deleteIndividualSpy = jest.spyOn(solutionEntryService, 'deleteSolutionEntry').mockReturnValue(new Observable((s) => s.next())); - comp.solutionEntries = solutionEntries; - - comp.deleteSolutionEntry(entry1); - - expect(deleteIndividualSpy).toHaveBeenCalledOnce(); - expect(deleteIndividualSpy).toHaveBeenCalledWith(1, 4, 2); - expect(onEntryUpdatedSpy).toHaveBeenCalledOnce(); - expect(onEntryUpdatedSpy).toHaveBeenCalledWith([entry2]); - expect(comp.solutionEntries).toEqual([entry2]); - }); - - it('should sort tests ascending from no order', () => { - comp.solutionEntries = [entry2, entry1]; - comp.changeTestCaseSortOrder(); - expect(comp.solutionEntries).toEqual([entry1, entry2]); - expect(comp.testCaseSortOrder).toBe(SortingOrder.ASCENDING); - }); - - it('should sort tests descending from ascending', () => { - comp.testCaseSortOrder = SortingOrder.ASCENDING; - comp.solutionEntries = [entry1, entry2]; - - comp.changeTestCaseSortOrder(); - - expect(comp.solutionEntries).toEqual([entry2, entry1]); - expect(comp.testCaseSortOrder).toBe(SortingOrder.DESCENDING); - }); - - it('should sort tests ascending from descending', () => { - comp.testCaseSortOrder = SortingOrder.DESCENDING; - comp.solutionEntries = [entry2, entry1]; - - comp.changeTestCaseSortOrder(); - - expect(comp.solutionEntries).toEqual([entry1, entry2]); - expect(comp.testCaseSortOrder).toBe(SortingOrder.ASCENDING); - }); -}); diff --git a/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-file.component.spec.ts b/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-file.component.spec.ts deleted file mode 100644 index a35ac8f46b3b..000000000000 --- a/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-file.component.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { ArtemisTestModule } from '../../../test.module'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { TestwiseCoverageFileComponent } from 'app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-file.component'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; -import { TestwiseCoverageReportEntry } from 'app/entities/hestia/testwise-coverage-report-entry.model'; -import { CoverageFileReport } from 'app/entities/hestia/coverage-file-report.model'; -import { MatExpansionModule } from '@angular/material/expansion'; -import { MockComponent } from 'ng-mocks'; -import { MonacoEditorComponent } from 'app/shared/monaco-editor/monaco-editor.component'; - -describe('TestwiseCoverageFile Component', () => { - let comp: TestwiseCoverageFileComponent; - let fixture: ComponentFixture; - let reportEntry1: TestwiseCoverageReportEntry; - let reportEntry2: TestwiseCoverageReportEntry; - let fileReport: CoverageFileReport; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, MatExpansionModule, NoopAnimationsModule], - declarations: [TestwiseCoverageFileComponent, MockComponent(MonacoEditorComponent)], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(TestwiseCoverageFileComponent); - comp = fixture.componentInstance; - - const testCase1 = { - id: 1, - testName: 'testBubbleSort()', - } as ProgrammingExerciseTestCase; - const testCase2 = { - id: 2, - testName: 'testBubbleSortForSmallLists()', - } as ProgrammingExerciseTestCase; - - reportEntry1 = { - id: 1, - startLine: 4, - lineCount: 2, - testCase: testCase1, - } as TestwiseCoverageReportEntry; - reportEntry2 = { - id: 2, - startLine: 7, - lineCount: 2, - testCase: testCase2, - } as TestwiseCoverageReportEntry; - - fileReport = { - id: 1, - lineCount: 10, - filePath: 'src/de/tum/in/ase/BubbleSort.java', - coveredLineCount: 5, - testwiseCoverageEntries: [reportEntry1, reportEntry2], - } as CoverageFileReport; - comp.fileContent = '\n\n\n\n\n\n\n\n\nlast'; - comp.fileName = 'src/de/tum/in/ase/BubbleSort.java'; - comp.fileReport = fileReport; - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should initially create and add all ranges correctly', () => { - const highlightLinesSpy = jest.spyOn(comp.editor, 'highlightLines'); - - comp.ngOnInit(); - - expect(highlightLinesSpy).toHaveBeenCalledTimes(2); - expect(highlightLinesSpy).toHaveBeenCalledWith( - 4, - 5, - TestwiseCoverageFileComponent.COVERED_LINE_HIGHLIGHT_CLASS, - TestwiseCoverageFileComponent.COVERED_LINE_HIGHLIGHT_CLASS, - ); - expect(highlightLinesSpy).toHaveBeenCalledWith( - 7, - 8, - TestwiseCoverageFileComponent.COVERED_LINE_HIGHLIGHT_CLASS, - TestwiseCoverageFileComponent.COVERED_LINE_HIGHLIGHT_CLASS, - ); - }); - - it('should calculate covered line ratio correctly', () => { - comp.fileReport.testwiseCoverageEntries = [reportEntry1]; - comp.ngOnInit(); - expect(comp.proportionCoveredLines).toBe(0.2); - expect(comp.proportionString).toBe('20.0 %'); - }); - - it('should update on input coverage data changes', () => { - comp.fileReport.testwiseCoverageEntries = [reportEntry1, reportEntry2]; - comp.ngOnInit(); - expect(comp.proportionCoveredLines).toBe(0.4); - expect(comp.proportionString).toBe('40.0 %'); - - const highlightLinesSpy = jest.spyOn(comp.editor, 'highlightLines'); - - fileReport.testwiseCoverageEntries = [reportEntry1]; - comp.fileReport = fileReport; - fixture.detectChanges(); - - expect(comp.proportionCoveredLines).toBe(0.2); - expect(comp.proportionString).toBe('20.0 %'); - expect(highlightLinesSpy).toHaveBeenCalledExactlyOnceWith( - 4, - 5, - TestwiseCoverageFileComponent.COVERED_LINE_HIGHLIGHT_CLASS, - TestwiseCoverageFileComponent.COVERED_LINE_HIGHLIGHT_CLASS, - ); - }); -}); diff --git a/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-report.component.spec.ts b/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-report.component.spec.ts deleted file mode 100644 index 1c2e97853b5b..000000000000 --- a/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-report.component.spec.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { ArtemisTestModule } from '../../../test.module'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { TestwiseCoverageReportComponent } from 'app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.component'; -import { CoverageReport } from 'app/entities/hestia/coverage-report.model'; -import { CoverageFileReport } from 'app/entities/hestia/coverage-file-report.model'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; -import { TestwiseCoverageReportEntry } from 'app/entities/hestia/testwise-coverage-report-entry.model'; - -describe('TestwiseCoverageReport Component', () => { - let comp: TestwiseCoverageReportComponent; - let fixture: ComponentFixture; - - let report: CoverageReport; - let reportEntries: TestwiseCoverageReportEntry[]; - let fileReports: CoverageFileReport[]; - let fileContentByName: Map; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - declarations: [TestwiseCoverageReportComponent], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(TestwiseCoverageReportComponent); - comp = fixture.componentInstance; - - const testCase1 = { - id: 1, - testName: 'testBubbleSort()', - } as ProgrammingExerciseTestCase; - const testCase2 = { - id: 2, - testName: 'testBubbleSortForSmallLists()', - } as ProgrammingExerciseTestCase; - const testCase3 = { - id: 3, - testName: 'testMergeSort()', - } as ProgrammingExerciseTestCase; - - const reportEntry1 = { - id: 1, - startLine: 4, - lineCount: 2, - testCase: testCase1, - } as TestwiseCoverageReportEntry; - const reportEntry2 = { - id: 2, - startLine: 7, - lineCount: 2, - testCase: testCase2, - } as TestwiseCoverageReportEntry; - const reportEntry3 = { - id: 3, - startLine: 1, - lineCount: 2, - testCase: testCase3, - } as TestwiseCoverageReportEntry; - reportEntries = [reportEntry1, reportEntry2, reportEntry3]; - - fileReports = [ - { - filePath: 'src/de/tum/in/ase/BubbleSort.java', - lineCount: 10, - coveredLineCount: 5, - testwiseCoverageEntries: [reportEntry1, reportEntry2], - } as CoverageFileReport, - { - filePath: 'src/de/tum/in/ase/MergeSort.java', - lineCount: 2, - coveredLineCount: 1, - testwiseCoverageEntries: [reportEntry3], - } as CoverageFileReport, - ]; - - report = { - id: 1, - fileReports, - coveredLineRatio: 0.5, - } as CoverageReport; - comp.report = report; - - fileContentByName = new Map(); - // file with 10 lines, 4 covered - fileContentByName.set( - 'src/de/tum/in/ase/BubbleSort.java', - 'package de.tum.in.ase;\n\ncovered\ncovered\nuncovered\ncovered\ncovered\nuncovered\nuncovered\nuncovered', - ); - // file with 2 lines, 2 covered - fileContentByName.set('src/de/tum/in/ase/MergeSort.java', 'covered\ncovered'); - comp.fileContentByPath = fileContentByName; - - comp.ngOnInit(); - }); - }); - - it('should initially display coverage for all test cases', () => { - const expectedMap = new Map(); - expectedMap.set('testBubbleSort()', true); - expectedMap.set('testBubbleSortForSmallLists()', true); - expectedMap.set('testMergeSort()', true); - - expect(comp.displayedTestCaseNames.size).toBe(3); - expect(comp.displayedTestCaseNames).toEqual(expectedMap); - }); - - it('should set file report by file name with for all test cases', () => { - const expectedMap = new Map(); - expectedMap.set('src/de/tum/in/ase/BubbleSort.java', fileReports[0]); - expectedMap.set('src/de/tum/in/ase/MergeSort.java', fileReports[1]); - - expect(comp.fileReportByFileName).toEqual(expectedMap); - }); - - it('should filter coverage file reports for test case', () => { - comp.changeReportsBySelectedTestCases('testBubbleSort()'); - expect(comp.displayedTestCaseNames.get('testBubbleSort()')).toBeFalse(); - expect(comp.fileReportByFileName.size).toBe(2); - expect(comp.fileReportByFileName.get('src/de/tum/in/ase/BubbleSort.java')?.testwiseCoverageEntries).toEqual([reportEntries[1]]); - }); - - it('should create empty file report if no report exists for file', () => { - comp.fileContentByPath.set('notexisting.java', '\n\n'); - comp.ngOnInit(); - - expect(comp.fileReportByFileName.get('notexisting.java')).toEqual({ - lineCount: 3, - coveredLineCount: 0, - filePath: 'notexisting.java', - testwiseCoverageEntries: [], - } as CoverageFileReport); - }); -}); diff --git a/src/test/javascript/spec/component/localci/build-agents/build-agent-details.component.spec.ts b/src/test/javascript/spec/component/localci/build-agents/build-agent-details.component.spec.ts index 606bc59a0ed8..7224aaf85123 100644 --- a/src/test/javascript/spec/component/localci/build-agents/build-agent-details.component.spec.ts +++ b/src/test/javascript/spec/component/localci/build-agents/build-agent-details.component.spec.ts @@ -59,7 +59,6 @@ describe('BuildAgentDetailsComponent', () => { projectType: 'Maven', scaEnabled: false, sequentialTestRunsEnabled: false, - testwiseCoverageEnabled: false, resultPaths: [], }; diff --git a/src/test/javascript/spec/component/localci/build-agents/build-agent-summary.component.spec.ts b/src/test/javascript/spec/component/localci/build-agents/build-agent-summary.component.spec.ts index 0d798185a915..623f92e6482f 100644 --- a/src/test/javascript/spec/component/localci/build-agents/build-agent-summary.component.spec.ts +++ b/src/test/javascript/spec/component/localci/build-agents/build-agent-summary.component.spec.ts @@ -56,7 +56,6 @@ describe('BuildAgentSummaryComponent', () => { projectType: 'Maven', scaEnabled: false, sequentialTestRunsEnabled: false, - testwiseCoverageEnabled: false, resultPaths: [], }; diff --git a/src/test/javascript/spec/component/localci/build-agents/build-agents.service.spec.ts b/src/test/javascript/spec/component/localci/build-agents/build-agents.service.spec.ts index 18b9f0135b83..69fb9bca2acd 100644 --- a/src/test/javascript/spec/component/localci/build-agents/build-agents.service.spec.ts +++ b/src/test/javascript/spec/component/localci/build-agents/build-agents.service.spec.ts @@ -42,7 +42,6 @@ describe('BuildAgentsService', () => { projectType: 'Maven', scaEnabled: false, sequentialTestRunsEnabled: false, - testwiseCoverageEnabled: false, resultPaths: [], }; diff --git a/src/test/javascript/spec/component/localci/build-queue/build-queue.component.spec.ts b/src/test/javascript/spec/component/localci/build-queue/build-queue.component.spec.ts index 4c53661e99d4..b22a67ee3f15 100644 --- a/src/test/javascript/spec/component/localci/build-queue/build-queue.component.spec.ts +++ b/src/test/javascript/spec/component/localci/build-queue/build-queue.component.spec.ts @@ -89,7 +89,6 @@ describe('BuildQueueComponent', () => { projectType: 'Maven', scaEnabled: false, sequentialTestRunsEnabled: false, - testwiseCoverageEnabled: false, resultPaths: [], }, }, @@ -125,7 +124,6 @@ describe('BuildQueueComponent', () => { projectType: 'Maven', scaEnabled: false, sequentialTestRunsEnabled: false, - testwiseCoverageEnabled: false, resultPaths: [], }, }, @@ -163,7 +161,6 @@ describe('BuildQueueComponent', () => { projectType: 'Maven', scaEnabled: false, sequentialTestRunsEnabled: false, - testwiseCoverageEnabled: false, resultPaths: [], }, }, @@ -199,7 +196,6 @@ describe('BuildQueueComponent', () => { projectType: 'Maven', scaEnabled: false, sequentialTestRunsEnabled: false, - testwiseCoverageEnabled: false, resultPaths: [], }, }, diff --git a/src/test/javascript/spec/component/localci/build-queue/build-queue.service.spec.ts b/src/test/javascript/spec/component/localci/build-queue/build-queue.service.spec.ts index 73b4ac460f63..16a2d81d37ed 100644 --- a/src/test/javascript/spec/component/localci/build-queue/build-queue.service.spec.ts +++ b/src/test/javascript/spec/component/localci/build-queue/build-queue.service.spec.ts @@ -80,7 +80,6 @@ describe('BuildQueueService', () => { buildConfig.projectType = 'type1'; buildConfig.scaEnabled = false; buildConfig.sequentialTestRunsEnabled = false; - buildConfig.testwiseCoverageEnabled = false; buildConfig.resultPaths = ['path1']; elem1.id = '1'; diff --git a/src/test/javascript/spec/component/localvc/commit-details-view.component.spec.ts b/src/test/javascript/spec/component/localvc/commit-details-view.component.spec.ts index fb964f7ea127..7625253cc587 100644 --- a/src/test/javascript/spec/component/localvc/commit-details-view.component.spec.ts +++ b/src/test/javascript/spec/component/localvc/commit-details-view.component.spec.ts @@ -12,10 +12,10 @@ import { MockComponent, MockPipe } from 'ng-mocks'; import { CommitDetailsViewComponent } from 'app/localvc/commit-details-view/commit-details-view.component'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { MockProgrammingExerciseService } from '../../helpers/mocks/service/mock-programming-exercise.service'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffReport } from '../../../../../main/webapp/app/entities/programming-exercise-git-diff-report.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; -import { GitDiffReportComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-report.component'; +import { GitDiffReportComponent } from '../../../../../main/webapp/app/exercises/programming/git-diff-report/git-diff-report.component'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { HttpResponse } from '@angular/common/http'; diff --git a/src/test/javascript/spec/component/overview/exercise-details/course-exercise-details.component.spec.ts b/src/test/javascript/spec/component/overview/exercise-details/course-exercise-details.component.spec.ts index 4f2a2c9b8331..cfe00096d639 100644 --- a/src/test/javascript/spec/component/overview/exercise-details/course-exercise-details.component.spec.ts +++ b/src/test/javascript/spec/component/overview/exercise-details/course-exercise-details.component.spec.ts @@ -59,7 +59,6 @@ import { LockRepositoryPolicy } from 'app/entities/submission-policy.model'; import { PlagiarismCasesService } from 'app/course/plagiarism-cases/shared/plagiarism-cases.service'; import { PlagiarismVerdict } from 'app/exercises/shared/plagiarism/types/PlagiarismVerdict'; import { AlertService } from 'app/core/util/alert.service'; -import { ExerciseHintButtonOverlayComponent } from 'app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component'; import { ProgrammingExerciseExampleSolutionRepoDownloadComponent } from 'app/exercises/programming/shared/actions/programming-exercise-example-solution-repo-download.component'; import { ResetRepoButtonComponent } from 'app/shared/components/reset-repo-button/reset-repo-button.component'; import { ProblemStatementComponent } from 'app/overview/exercise-details/problem-statement/problem-statement.component'; @@ -71,7 +70,6 @@ import { ScienceService } from 'app/shared/science/science.service'; import { MockScienceService } from '../../../helpers/mocks/service/mock-science-service'; import { ScienceEventType } from 'app/shared/science/science.model'; import { PROFILE_IRIS } from 'app/app.constants'; -import { ExerciseHintService } from 'app/exercises/shared/exercise-hint/shared/exercise-hint.service'; import { CourseInformationSharingConfiguration } from 'app/entities/course.model'; import { provideHttpClient } from '@angular/common/http'; @@ -80,15 +78,12 @@ describe('CourseExerciseDetailsComponent', () => { let fixture: ComponentFixture; let profileService: ProfileService; let exerciseService: ExerciseService; - let exerciseHintService: ExerciseHintService; let teamService: TeamService; let participationService: ParticipationService; let participationWebsocketService: ParticipationWebsocketService; let complaintService: ComplaintService; let getProfileInfoMock: jest.SpyInstance; let getExerciseDetailsMock: jest.SpyInstance; - let getActivatedExerciseHintsMock: jest.SpyInstance; - let getAvailableExerciseHintsMock: jest.SpyInstance; let mergeStudentParticipationMock: jest.SpyInstance; let subscribeForParticipationChangesMock: jest.SpyInstance; let participationWebsockerBehaviourSubject: BehaviorSubject; @@ -152,7 +147,6 @@ describe('CourseExerciseDetailsComponent', () => { MockComponent(ResultHistoryComponent), MockComponent(ResultComponent), MockComponent(ComplaintsStudentViewComponent), - MockComponent(ExerciseHintButtonOverlayComponent), MockComponent(ProgrammingExerciseExampleSolutionRepoDownloadComponent), MockComponent(ProblemStatementComponent), MockComponent(ResetRepoButtonComponent), @@ -189,7 +183,6 @@ describe('CourseExerciseDetailsComponent', () => { MockProvider(PlagiarismCasesService), MockProvider(AlertService), MockProvider(IrisSettingsService), - MockProvider(ExerciseHintService), ], }) .compileComponents() @@ -231,10 +224,6 @@ describe('CourseExerciseDetailsComponent', () => { scienceService = TestBed.inject(ScienceService); logEventStub = jest.spyOn(scienceService, 'logEvent'); - exerciseHintService = TestBed.inject(ExerciseHintService); - getActivatedExerciseHintsMock = jest.spyOn(exerciseHintService, 'getActivatedExerciseHints'); - getAvailableExerciseHintsMock = jest.spyOn(exerciseHintService, 'getAvailableExerciseHints'); - participationService = TestBed.inject(ParticipationService); mergeStudentParticipationMock = jest.spyOn(participationService, 'mergeStudentParticipations'); }); @@ -335,16 +324,6 @@ describe('CourseExerciseDetailsComponent', () => { expect(comp.exampleSolutionCollapsed).toBeFalse(); }); - it('should activate hint', () => { - comp.availableExerciseHints = [{ id: 1 }, { id: 2 }]; - comp.activatedExerciseHints = []; - - const activatedHint = comp.availableExerciseHints[0]; - comp.onHintActivated(activatedHint); - expect(comp.availableExerciseHints).not.toContain(activatedHint); - expect(comp.activatedExerciseHints).toContain(activatedHint); - }); - it('should sort results by completion date in ascending order', () => { const result1 = { completionDate: dayjs().subtract(2, 'days') } as Result; const result2 = { completionDate: dayjs().subtract(1, 'day') } as Result; @@ -432,16 +411,9 @@ describe('CourseExerciseDetailsComponent', () => { const newParticipation = { ...participation, submissions: [submission, { id: submissionId + 1 }] }; - getActivatedExerciseHintsMock.mockReturnValue(of({ body: [] })); - getAvailableExerciseHintsMock.mockReturnValue(of({ body: [] })); mergeStudentParticipationMock.mockReturnValue([newParticipation]); participationWebsockerBehaviourSubject.next({ ...newParticipation, exercise: programmingExercise, results: [] }); - - tick(); - - expect(getActivatedExerciseHintsMock).toHaveBeenCalledOnce(); - expect(getAvailableExerciseHintsMock).toHaveBeenCalledOnce(); })); it.each<[string[]]>([[[]], [[PROFILE_IRIS]]])( diff --git a/src/test/javascript/spec/component/programming-diff-report-detail.component.spec.ts b/src/test/javascript/spec/component/programming-diff-report-detail.component.spec.ts index 9f1940740640..9c0ede8c5141 100644 --- a/src/test/javascript/spec/component/programming-diff-report-detail.component.spec.ts +++ b/src/test/javascript/spec/component/programming-diff-report-detail.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { MockNgbModalService } from '../helpers/mocks/service/mock-ngb-modal.service'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffReport } from '../../../../main/webapp/app/entities/programming-exercise-git-diff-report.model'; import { ProgrammingDiffReportDetailComponent } from 'app/detail-overview-list/components/programming-diff-report-detail/programming-diff-report-detail.component'; import { TranslateService } from '@ngx-translate/core'; import { MockTranslateService } from '../helpers/mocks/service/mock-translate.service'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-aeolus-build-plan.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-aeolus-build-plan.component.spec.ts index 005fa8fdd41a..c1882b341c37 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-aeolus-build-plan.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-aeolus-build-plan.component.spec.ts @@ -179,7 +179,6 @@ describe('ProgrammingExercise Aeolus Custom Build Plan', () => { comp.projectType = programmingExercise.projectType; comp.sequentialTestRuns = programmingExercise.buildConfig?.sequentialTestRuns; comp.staticCodeAnalysisEnabled = programmingExercise.staticCodeAnalysisEnabled; - comp.testwiseCoverageEnabled = programmingExercise.buildConfig?.testwiseCoverageEnabled; expect(comp.shouldReloadTemplate()).toBeFalse(); }); @@ -188,7 +187,6 @@ describe('ProgrammingExercise Aeolus Custom Build Plan', () => { comp.projectType = ProjectType.PLAIN_GRADLE; comp.sequentialTestRuns = programmingExercise.buildConfig?.sequentialTestRuns; comp.staticCodeAnalysisEnabled = true; - comp.testwiseCoverageEnabled = true; expect(comp.shouldReloadTemplate()).toBeTrue(); }); @@ -214,7 +212,6 @@ describe('ProgrammingExercise Aeolus Custom Build Plan', () => { comp.projectType = undefined; comp.sequentialTestRuns = undefined; comp.staticCodeAnalysisEnabled = undefined; - comp.testwiseCoverageEnabled = undefined; comp.programmingExerciseCreationConfig = programmingExerciseCreationConfigMock; comp.programmingExerciseCreationConfig.customBuildPlansSupported = PROFILE_AEOLUS; comp.loadAeolusTemplate(); @@ -222,7 +219,6 @@ describe('ProgrammingExercise Aeolus Custom Build Plan', () => { expect(comp.projectType).toBe(programmingExercise.projectType); expect(comp.sequentialTestRuns).toBe(programmingExercise.buildConfig?.sequentialTestRuns); expect(comp.staticCodeAnalysisEnabled).toBe(programmingExercise.staticCodeAnalysisEnabled); - expect(comp.testwiseCoverageEnabled).toBe(programmingExercise.buildConfig?.testwiseCoverageEnabled); }); it('should not call loadAeolusTemplate', () => { diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-build-plan.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-build-plan.component.spec.ts index a02862b32a59..2e9e9b841e50 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-build-plan.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-build-plan.component.spec.ts @@ -98,7 +98,6 @@ describe('ProgrammingExercise Custom Build Plan', () => { comp.projectType = programmingExercise.projectType; comp.sequentialTestRuns = programmingExercise.buildConfig?.sequentialTestRuns; comp.staticCodeAnalysisEnabled = programmingExercise.staticCodeAnalysisEnabled; - comp.testwiseCoverageEnabled = programmingExercise.buildConfig?.testwiseCoverageEnabled; expect(comp.shouldReloadTemplate()).toBeFalse(); }); @@ -107,7 +106,6 @@ describe('ProgrammingExercise Custom Build Plan', () => { comp.projectType = ProjectType.PLAIN_GRADLE; comp.sequentialTestRuns = programmingExercise.buildConfig?.sequentialTestRuns; comp.staticCodeAnalysisEnabled = true; - comp.testwiseCoverageEnabled = true; expect(comp.shouldReloadTemplate()).toBeTrue(); }); @@ -133,7 +131,6 @@ describe('ProgrammingExercise Custom Build Plan', () => { comp.projectType = undefined; comp.sequentialTestRuns = undefined; comp.staticCodeAnalysisEnabled = undefined; - comp.testwiseCoverageEnabled = undefined; comp.programmingExerciseCreationConfig = programmingExerciseCreationConfigMock; comp.programmingExerciseCreationConfig.customBuildPlansSupported = PROFILE_LOCALCI; comp.loadAeolusTemplate(); @@ -141,7 +138,6 @@ describe('ProgrammingExercise Custom Build Plan', () => { expect(comp.projectType).toBe(programmingExercise.projectType); expect(comp.sequentialTestRuns).toBe(programmingExercise.buildConfig?.sequentialTestRuns); expect(comp.staticCodeAnalysisEnabled).toBe(programmingExercise.staticCodeAnalysisEnabled); - expect(comp.testwiseCoverageEnabled).toBe(programmingExercise.buildConfig?.testwiseCoverageEnabled); }); it('should not call loadAeolusTemplate', () => { diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-detail.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-detail.component.spec.ts index dba7c6b0a9ca..062554dd9c5f 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-detail.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-detail.component.spec.ts @@ -22,7 +22,6 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockProgrammingExerciseGradingService } from '../../helpers/mocks/service/mock-programming-exercise-grading.service'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; @@ -32,8 +31,7 @@ import { ProgrammingLanguageFeatureService, } from 'app/exercises/programming/shared/service/programming-language-feature/programming-language-feature.service'; import { MockRouter } from '../../helpers/mocks/mock-router'; -import { BuildConfig } from '../../../../../main/webapp/app/entities/programming/build-config.model'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffReport } from '../../../../../main/webapp/app/entities/programming-exercise-git-diff-report.model'; import { BuildLogStatisticsDTO } from 'app/entities/programming/build-log-statistics-dto'; import { SubmissionPolicyService } from '../../../../../main/webapp/app/exercises/programming/manage/services/submission-policy.service'; @@ -64,9 +62,6 @@ describe('ProgrammingExerciseDetailComponent', () => { solutionParticipation: { id: 2, } as SolutionProgrammingExerciseParticipation, - buildConfig: { - testwiseCoverageEnabled: true, - } as BuildConfig, } as ProgrammingExercise; const exerciseStatistics = { @@ -163,14 +158,12 @@ describe('ProgrammingExerciseDetailComponent', () => { it('should reload on participation change', fakeAsync(() => { const loadDiffSpy = jest.spyOn(comp, 'loadGitDiffReport'); jest.spyOn(exerciseService, 'getLatestResult').mockReturnValue({ successful: true }); - jest.spyOn(exerciseService, 'getLatestFullTestwiseCoverageReport').mockReturnValue(of({ coveredLineRatio: 0.5 })); comp.programmingExercise = mockProgrammingExercise; comp.programmingExerciseBuildConfig = mockProgrammingExercise.buildConfig; comp.onParticipationChange(); tick(); expect(loadDiffSpy).toHaveBeenCalledOnce(); expect(gitDiffReportStub).toHaveBeenCalledOnce(); - expect(comp.programmingExercise.coveredLinesRatio).toBe(0.5); })); describe('onInit for course exercise', () => { @@ -275,42 +268,6 @@ describe('ProgrammingExerciseDetailComponent', () => { expect(sections).toBeDefined(); }); - it('should create structural solution entries', () => { - const programmingExercise = new ProgrammingExercise(new Course(), undefined); - programmingExercise.id = 123; - comp.programmingExercise = programmingExercise; - - jest.spyOn(exerciseService, 'createStructuralSolutionEntries').mockReturnValue(of([] as ProgrammingExerciseSolutionEntry[])); - jest.spyOn(alertService, 'addAlert'); - - comp.createStructuralSolutionEntries(); - - expect(exerciseService.createStructuralSolutionEntries).toHaveBeenCalledOnce(); - expect(alertService.addAlert).toHaveBeenCalledOnce(); - expect(alertService.addAlert).toHaveBeenCalledWith({ - type: AlertType.SUCCESS, - message: 'artemisApp.programmingExercise.createStructuralSolutionEntriesSuccess', - }); - }); - - it('should create behavioral solution entries', () => { - const programmingExercise = new ProgrammingExercise(new Course(), undefined); - programmingExercise.id = 123; - comp.programmingExercise = programmingExercise; - - jest.spyOn(exerciseService, 'createBehavioralSolutionEntries').mockReturnValue(of([] as ProgrammingExerciseSolutionEntry[])); - jest.spyOn(alertService, 'addAlert'); - - comp.createBehavioralSolutionEntries(); - - expect(exerciseService.createBehavioralSolutionEntries).toHaveBeenCalledOnce(); - expect(alertService.addAlert).toHaveBeenCalledOnce(); - expect(alertService.addAlert).toHaveBeenCalledWith({ - type: AlertType.SUCCESS, - message: 'artemisApp.programmingExercise.createBehavioralSolutionEntriesSuccess', - }); - }); - it.each([ ['jenkins', true], ['gitlabci', true], diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-update.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-update.component.spec.ts index c4cbdf912f8a..3de1c9fe3eb5 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-update.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-update.component.spec.ts @@ -197,7 +197,6 @@ describe('ProgrammingExerciseUpdateComponent', () => { packageNameRequired: true, checkoutSolutionRepositoryAllowed: true, projectTypes: [ProjectType.PLAIN_MAVEN, ProjectType.MAVEN_MAVEN], - testwiseCoverageAnalysisSupported: true, auxiliaryRepositoriesSupported: true, } as ProgrammingLanguageFeature); }); @@ -1195,7 +1194,6 @@ describe('ProgrammingExerciseUpdateComponent', () => { tick(); expect(comp.programmingExercise.staticCodeAnalysisEnabled).toBeFalse(); - expect(comp.programmingExercise.buildConfig?.testwiseCoverageEnabled).toBeFalse(); })); it('should disable options for java dejagnu project type and re-enable them after changing back to maven or gradle', fakeAsync(() => { @@ -1203,19 +1201,15 @@ describe('ProgrammingExerciseUpdateComponent', () => { getFeaturesStub.mockImplementation((language: ProgrammingLanguage) => getProgrammingLanguageFeature(language)); comp.selectedProjectType = ProjectType.MAVEN_BLACKBOX; expect(comp.sequentialTestRunsAllowed).toBeFalse(); - expect(comp.testwiseCoverageAnalysisSupported).toBeFalse(); comp.selectedProjectType = ProjectType.MAVEN_MAVEN; expect(comp.sequentialTestRunsAllowed).toBeTrue(); - expect(comp.testwiseCoverageAnalysisSupported).toBeTrue(); comp.selectedProjectType = ProjectType.MAVEN_BLACKBOX; expect(comp.sequentialTestRunsAllowed).toBeFalse(); - expect(comp.testwiseCoverageAnalysisSupported).toBeFalse(); comp.selectedProjectType = ProjectType.GRADLE_GRADLE; expect(comp.sequentialTestRunsAllowed).toBeTrue(); - expect(comp.testwiseCoverageAnalysisSupported).toBeTrue(); })); }); @@ -1392,7 +1386,6 @@ const getProgrammingLanguageFeature = (programmingLanguage: ProgrammingLanguage) packageNameRequired: true, checkoutSolutionRepositoryAllowed: false, projectTypes: [ProjectType.PLAIN, ProjectType.XCODE], - testwiseCoverageAnalysisSupported: false, auxiliaryRepositoriesSupported: true, } as ProgrammingLanguageFeature; case ProgrammingLanguage.JAVA: @@ -1404,7 +1397,6 @@ const getProgrammingLanguageFeature = (programmingLanguage: ProgrammingLanguage) packageNameRequired: true, checkoutSolutionRepositoryAllowed: true, projectTypes: [ProjectType.PLAIN_MAVEN, ProjectType.MAVEN_MAVEN], - testwiseCoverageAnalysisSupported: true, auxiliaryRepositoriesSupported: true, } as ProgrammingLanguageFeature; case ProgrammingLanguage.HASKELL: @@ -1415,7 +1407,6 @@ const getProgrammingLanguageFeature = (programmingLanguage: ProgrammingLanguage) plagiarismCheckSupported: false, packageNameRequired: false, checkoutSolutionRepositoryAllowed: true, - testwiseCoverageAnalysisSupported: false, auxiliaryRepositoriesSupported: true, } as ProgrammingLanguageFeature; case ProgrammingLanguage.C: @@ -1427,7 +1418,6 @@ const getProgrammingLanguageFeature = (programmingLanguage: ProgrammingLanguage) packageNameRequired: false, checkoutSolutionRepositoryAllowed: true, projectTypes: [ProjectType.FACT, ProjectType.GCC], - testwiseCoverageAnalysisSupported: false, auxiliaryRepositoriesSupported: true, } as ProgrammingLanguageFeature; default: diff --git a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-creation-config-mock.ts b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-creation-config-mock.ts index d1f3ef607ed6..36987b972c7d 100644 --- a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-creation-config-mock.ts +++ b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-creation-config-mock.ts @@ -51,7 +51,6 @@ export const programmingExerciseCreationConfigMock: ProgrammingExerciseCreationC staticCodeAnalysisAllowed: false, supportedLanguages: [], templateParticipationResultLoaded: false, - testwiseCoverageAnalysisSupported: false, titleNamePattern: '', updateCategories(_categories: ExerciseCategory[]): void {}, updateCheckoutDirectory(_editedAuxiliaryRepository: AuxiliaryRepository): (newValue: any) => string | undefined { diff --git a/src/test/javascript/spec/component/shared/http/file.service.spec.ts b/src/test/javascript/spec/component/shared/http/file.service.spec.ts index 843fd61a12d3..c8657cd9a70f 100644 --- a/src/test/javascript/spec/component/shared/http/file.service.spec.ts +++ b/src/test/javascript/spec/component/shared/http/file.service.spec.ts @@ -136,51 +136,12 @@ describe('FileService', () => { }); }); - describe('getAeolusTemplateFile', () => { - it('should fetch the aeolus template file with all parameters', () => { - const language = ProgrammingLanguage.PYTHON; - const projectType = ProjectType.PLAIN; - const staticAnalysis = true; - const sequentialRuns = false; - const coverage = true; - const expectedUrl = `api/files/aeolus/templates/PYTHON/PLAIN?staticAnalysis=true&sequentialRuns=false&testCoverage=true`; - const response = 'aeolus template content'; - - fileService.getAeolusTemplateFile(language, projectType, staticAnalysis, sequentialRuns, coverage).subscribe((data) => { - expect(data).toEqual(response); - }); - - const req = httpMock.expectOne({ - url: expectedUrl, - method: 'GET', - }); - expect(req.request.responseType).toBe('text'); - req.flush(response); - }); - - it('should fetch the aeolus template file with missing optional parameters', () => { - const expectedUrl = `api/files/aeolus/templates/PYTHON?staticAnalysis=false&sequentialRuns=false&testCoverage=false`; - const response = 'aeolus template content'; - - fileService.getAeolusTemplateFile(ProgrammingLanguage.PYTHON).subscribe((data) => { - expect(data).toEqual(response); - }); - - const req = httpMock.expectOne({ - url: expectedUrl, - method: 'GET', - }); - expect(req.request.responseType).toBe('text'); - req.flush(response); - }); - }); - describe('getTemplateCodeOfConduct', () => { it('should fetch the template code of conduct', () => { const expectedUrl = `api/files/templates/code-of-conduct`; const response = 'code of conduct content'; - fileService.getTemplateCodeOfCondcut().subscribe((data) => { + fileService.getTemplateCodeOfConduct().subscribe((data) => { expect(data.body).toEqual(response); }); diff --git a/src/test/javascript/spec/component/shared/navbar.component.spec.ts b/src/test/javascript/spec/component/shared/navbar.component.spec.ts index bc2e7cb37b92..5de3b28f3e38 100644 --- a/src/test/javascript/spec/component/shared/navbar.component.spec.ts +++ b/src/test/javascript/spec/component/shared/navbar.component.spec.ts @@ -1,11 +1,10 @@ -import { HttpResponse, provideHttpClient } from '@angular/common/http'; +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { ActivatedRoute, Router, UrlSerializer } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { ProfileInfo } from 'app/shared/layouts/profiles/profile-info.model'; -import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { GuidedTourComponent } from 'app/guided-tour/guided-tour.component'; import { HasAnyAuthorityDirective } from 'app/shared/auth/has-any-authority.directive'; import { FindLanguageFromKeyPipe } from 'app/shared/language/find-language-from-key.pipe'; @@ -21,7 +20,6 @@ import { of } from 'rxjs'; import { MockRouter } from '../../helpers/mocks/mock-router'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { MockRouterLinkActiveOptionsDirective, MockRouterLinkDirective } from '../../helpers/mocks/directive/mock-router-link.directive'; -import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { JhiConnectionWarningComponent } from 'app/shared/connection-warning/connection-warning.component'; import { AccountService } from 'app/core/auth/account.service'; import { MockAccountService } from '../../helpers/mocks/service/mock-account.service'; @@ -52,7 +50,6 @@ describe('NavbarComponent', () => { let fixture: ComponentFixture; let component: NavbarComponent; let entityTitleServiceStub: jest.SpyInstance; - let exerciseService: ExerciseService; let entityTitleService: EntityTitleService; let examParticipationService: ExamParticipationService; @@ -154,8 +151,6 @@ describe('NavbarComponent', () => { entityTitleServiceStub = jest .spyOn(entityTitleService, 'getTitle') .mockImplementation((type) => of('Test ' + type.substring(0, 1) + type.substring(1).toLowerCase())); - - exerciseService = fixture.debugElement.injector.get(ExerciseService); }); }); @@ -443,57 +438,6 @@ describe('NavbarComponent', () => { expect(component.breadcrumbs[4]).toEqual(assessmentCrumb); }); - it('programming exercise hints', () => { - const testUrl = '/course-management/1/exercises/2/exercise-hints/3'; - router.setUrl(testUrl); - - const findStub = jest.spyOn(exerciseService, 'find').mockReturnValue( - of({ - body: { - title: 'Test Exercise', - type: ExerciseType.PROGRAMMING, - }, - } as HttpResponse), - ); - - fixture.detectChanges(); - - expect(entityTitleServiceStub).toHaveBeenCalledTimes(2); - expect(entityTitleServiceStub).toHaveBeenCalledWith(EntityType.COURSE, [1]); - expect(entityTitleServiceStub).toHaveBeenCalledWith(EntityType.HINT, [3, 2]); - expect(findStub).toHaveBeenCalledOnce(); - expect(findStub).toHaveBeenCalledWith(2); - - const hintsCrumb = { - label: 'artemisApp.exerciseHint.home.title', - translate: true, - uri: '/course-management/1/exercises/2/exercise-hints/', - } as MockBreadcrumb; - - const hintCrumb = { - label: 'Test Hint', - translate: false, - uri: '/course-management/1/exercises/2/exercise-hints/3/', - } as MockBreadcrumb; - - expect(component.breadcrumbs).toHaveLength(6); - - expect(component.breadcrumbs[0]).toEqual(courseManagementCrumb); - expect(component.breadcrumbs[1]).toEqual(testCourseCrumb); - expect(component.breadcrumbs[2]).toEqual({ - label: 'artemisApp.course.exercises', - translate: true, - uri: '/course-management/1/exercises/', - } as MockBreadcrumb); - expect(component.breadcrumbs[3]).toEqual({ - label: 'Test Exercise', - translate: false, - uri: '/course-management/1/programming-exercises/2/', - } as MockBreadcrumb); - expect(component.breadcrumbs[4]).toEqual(hintsCrumb); - expect(component.breadcrumbs[5]).toEqual(hintCrumb); - }); - it('exercise assessment dashboard', () => { const courseId = 1; const exerciseId = 2; diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-exercise-hint.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-exercise-hint.service.ts deleted file mode 100644 index a3767d2437d0..000000000000 --- a/src/test/javascript/spec/helpers/mocks/service/mock-exercise-hint.service.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { HttpResponse } from '@angular/common/http'; -import { Observable, of } from 'rxjs'; -import { ExerciseHintResponse, IExerciseHintService } from 'app/exercises/shared/exercise-hint/shared/exercise-hint.service'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; - -export class MockExerciseHintService implements IExerciseHintService { - private exerciseHintDummy = { id: 1 } as ExerciseHint; - private exerciseHintDummy2 = { id: 2 } as ExerciseHint; - - create(exerciseId: number, exerciseHint: ExerciseHint): Observable { - return of({ body: this.exerciseHintDummy }) as Observable; - } - - delete(exerciseId: number, exerciseHintId: number): Observable> { - return of(); - } - - find(exerciseId: number, exerciseHintId: number): Observable { - return of({ body: this.exerciseHintDummy }) as Observable; - } - - findByExerciseId(exerciseId: number): Observable> { - return of({ body: [this.exerciseHintDummy, this.exerciseHintDummy2] }) as Observable>; - } - - update(exerciseId: number, exerciseHint: ExerciseHint): Observable { - return of({ body: this.exerciseHintDummy }) as Observable; - } - - activateExerciseHint(exerciseId: number, exerciseHintId: number): Observable { - return of(); - } - - getActivatedExerciseHints(exerciseId: number): Observable> { - return of(); - } - - getAvailableExerciseHints(exerciseId: number): Observable> { - return of(); - } - - getTitle(exerciseId: number, exerciseHintId: number): Observable> { - return of(); - } - - rateExerciseHint(exerciseId: number, exerciseHintId: number, ratingValue: number): Observable> { - return of(); - } -} diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise.service.ts index 27c111ba7665..70860b29e7c1 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise.service.ts @@ -2,7 +2,6 @@ import { of } from 'rxjs'; import { ProgrammingExerciseInstructorRepositoryType } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { Participation } from 'app/entities/participation/participation.model'; import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; -import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; export class MockProgrammingExerciseService { updateProblemStatement = (exerciseId: number, problemStatement: string) => of(); @@ -16,14 +15,9 @@ export class MockProgrammingExerciseService { exportInstructorRepository = (exerciseId: number, repositoryType: ProgrammingExerciseInstructorRepositoryType) => of({ body: undefined }); exportStudentRepository = (exerciseId: number, participationId: number) => of({ body: undefined }); exportStudentRequestedRepository = (exerciseId: number, includeTests: boolean) => of({ body: undefined }); - getTasksAndTestsExtractedFromProblemStatement = (exerciseId: number) => of(); - deleteTasksWithSolutionEntries = (exerciseId: number) => of(); getDiffReport = (exerciseId: number) => of({}); getBuildLogStatistics = (exerciseId: number) => of({}); - createStructuralSolutionEntries = (exerciseId: number) => of({}); - createBehavioralSolutionEntries = (exerciseId: number) => of({}); getLatestResult = (participation: Participation) => of({}); - getLatestFullTestwiseCoverageReport = (exerciseId: number) => of({}); combineTemplateRepositoryCommits = (exerciseId: number) => of({}); delete = (programmingExerciseId: number, deleteStudentReposBuildPlans: boolean, deleteBaseReposBuildPlans: boolean) => of({}); generateStructureOracle = (exerciseId: number) => of({}); diff --git a/src/test/javascript/spec/integration/code-editor/code-editor-student.integration.spec.ts b/src/test/javascript/spec/integration/code-editor/code-editor-student.integration.spec.ts index 5df8e8e32ce3..6b3b8441f2e7 100644 --- a/src/test/javascript/spec/integration/code-editor/code-editor-student.integration.spec.ts +++ b/src/test/javascript/spec/integration/code-editor/code-editor-student.integration.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ExerciseHintButtonOverlayComponent } from 'app/exercises/shared/exercise-hint/participate/exercise-hint-button-overlay.component'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import dayjs from 'dayjs/esm'; import { JhiLanguageHelper } from 'app/core/language/language.helper'; @@ -107,7 +106,6 @@ describe('CodeEditorStudentIntegration', () => { MockComponent(CodeEditorFileBrowserFolderComponent), MockComponent(CodeEditorFileBrowserFileComponent), MockComponent(CodeEditorStatusComponent), - MockComponent(ExerciseHintButtonOverlayComponent), TreeviewComponent, ], providers: [ diff --git a/src/test/javascript/spec/service/entity-title.service.spec.ts b/src/test/javascript/spec/service/entity-title.service.spec.ts index a0d25c344ae6..7bba4142c05a 100644 --- a/src/test/javascript/spec/service/entity-title.service.spec.ts +++ b/src/test/javascript/spec/service/entity-title.service.spec.ts @@ -38,7 +38,6 @@ describe('EntityTitleService', () => { }); it.each([ - { type: EntityType.HINT, ids: [3, 2], url: 'programming-exercises/2/exercise-hints/3' }, { type: EntityType.EXERCISE, ids: [1], url: 'exercises/1' }, { type: EntityType.LECTURE, ids: [1], url: 'lectures/1' }, { type: EntityType.COURSE, ids: [1], url: 'courses/1' }, @@ -82,7 +81,6 @@ describe('EntityTitleService', () => { it.each([ { type: EntityType.COURSE, ids: [] }, { type: EntityType.COURSE, ids: [undefined] }, - { type: EntityType.HINT, ids: [1, undefined] }, { type: undefined, ids: [undefined] }, { type: undefined, ids: [] }, ])('captures an exception if invalid parameters are supplied to getTitle', ({ type, ids }) => { @@ -100,12 +98,10 @@ describe('EntityTitleService', () => { it.each([ { type: EntityType.COURSE, ids: [], title: 'Test' }, { type: EntityType.COURSE, ids: [undefined], title: 'Test' }, - { type: EntityType.HINT, ids: [1, undefined], title: 'Test' }, { type: undefined, ids: [undefined], title: 'Test' }, { type: undefined, ids: [], title: 'Test' }, { type: EntityType.COURSE, ids: [], title: undefined }, { type: EntityType.COURSE, ids: [undefined], title: undefined }, - { type: EntityType.HINT, ids: [1, undefined], title: undefined }, { type: undefined, ids: [undefined], title: undefined }, { type: undefined, ids: [], title: undefined }, ])('captures an exception if invalid parameters are supplied to setTitle', ({ type, ids, title }) => { diff --git a/src/test/javascript/spec/service/exercise-hint.service.spec.ts b/src/test/javascript/spec/service/exercise-hint.service.spec.ts deleted file mode 100644 index b988b61db162..000000000000 --- a/src/test/javascript/spec/service/exercise-hint.service.spec.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; -import { HttpResponse, provideHttpClient } from '@angular/common/http'; -import { take } from 'rxjs/operators'; -import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; -import { MockExerciseService } from '../helpers/mocks/service/mock-exercise.service'; -import { ExerciseHintService } from 'app/exercises/shared/exercise-hint/shared/exercise-hint.service'; - -describe('ExerciseHint Service', () => { - let service: ExerciseHintService; - let httpMock: HttpTestingController; - let elemDefault: ExerciseHint; - let exerciseHint: ExerciseHint; - let expectedResult: any; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [], - providers: [provideHttpClient(), provideHttpClientTesting(), { provide: ExerciseService, useClass: MockExerciseService }], - }); - expectedResult = {} as HttpResponse; - service = TestBed.inject(ExerciseHintService); - httpMock = TestBed.inject(HttpTestingController); - - const exercise = new ProgrammingExercise(undefined, undefined); - exercise.id = 1; - - elemDefault = new ExerciseHint(); - elemDefault.id = 0; - elemDefault.title = 'AAAAAAA'; - elemDefault.content = 'AAAAAAA'; - elemDefault.exercise = exercise; - - exerciseHint = new ExerciseHint(); - exerciseHint.title = 'AAAAA'; - exerciseHint.content = 'BBBBB'; - exerciseHint.exercise = exercise; - }); - - describe('Service methods', () => { - it('should find an element', () => { - const returnedFromService = Object.assign({}, elemDefault); - service - .find(1, 123) - .pipe(take(1)) - .subscribe((resp) => (expectedResult = resp)); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(returnedFromService); - expect(expectedResult.body).toEqual(elemDefault); - }); - }); - - it('should create an ExerciseHint', () => { - const returnedFromService = Object.assign( - { - id: 0, - title: 'AAAAA', - content: 'BBBBBB', - exercise: { - id: 1, - }, - }, - elemDefault, - ); - const expected = Object.assign({}, returnedFromService); - service - .create(1, exerciseHint) - .pipe(take(1)) - .subscribe((resp) => (expectedResult = resp)); - const req = httpMock.expectOne({ method: 'POST' }); - req.flush(returnedFromService); - expect(expectedResult.body).toEqual(expected); - }); - - it('should update an ExerciseHint', () => { - const returnedFromService = Object.assign( - { - title: 'BBBBBB', - content: 'BBBBBB', - exercise: { - id: 1, - }, - }, - elemDefault, - ); - - const expected = Object.assign({}, returnedFromService); - service - .update(1, elemDefault) - .pipe(take(1)) - .subscribe((resp) => (expectedResult = resp)); - const req = httpMock.expectOne({ method: 'PUT' }); - req.flush(returnedFromService); - expect(expectedResult.body).toEqual(expected); - }); - - it('should delete an ExerciseHint', () => { - service.delete(1, 123).subscribe((resp) => (expectedResult = resp.ok)); - - const req = httpMock.expectOne({ method: 'DELETE' }); - req.flush({ status: 200 }); - expect(expectedResult).toBeTrue(); - }); - - afterEach(() => { - httpMock.verify(); - }); -}); diff --git a/src/test/javascript/spec/service/profile.service.spec.ts b/src/test/javascript/spec/service/profile.service.spec.ts index a210b70eeed0..aeb91a792501 100644 --- a/src/test/javascript/spec/service/profile.service.spec.ts +++ b/src/test/javascript/spec/service/profile.service.spec.ts @@ -76,7 +76,6 @@ describe('ProfileService', () => { packageNameRequired: true, checkoutSolutionRepositoryAllowed: false, projectTypes: [], - testwiseCoverageAnalysisSupported: true, auxiliaryRepositoriesSupported: true, }, { @@ -87,7 +86,6 @@ describe('ProfileService', () => { packageNameRequired: false, checkoutSolutionRepositoryAllowed: false, projectTypes: [], - testwiseCoverageAnalysisSupported: false, auxiliaryRepositoriesSupported: true, }, { @@ -98,7 +96,6 @@ describe('ProfileService', () => { packageNameRequired: true, checkoutSolutionRepositoryAllowed: false, projectTypes: ['PLAIN', 'XCODE'], - testwiseCoverageAnalysisSupported: false, auxiliaryRepositoriesSupported: true, }, { @@ -109,7 +106,6 @@ describe('ProfileService', () => { packageNameRequired: false, checkoutSolutionRepositoryAllowed: false, projectTypes: [], - testwiseCoverageAnalysisSupported: false, auxiliaryRepositoriesSupported: true, }, { @@ -120,7 +116,6 @@ describe('ProfileService', () => { packageNameRequired: true, checkoutSolutionRepositoryAllowed: false, projectTypes: ['PLAIN_MAVEN', 'MAVEN_MAVEN'], - testwiseCoverageAnalysisSupported: true, auxiliaryRepositoriesSupported: true, }, ], @@ -203,7 +198,6 @@ describe('ProfileService', () => { projectTypes: [], sequentialTestRuns: true, staticCodeAnalysis: false, - testwiseCoverageAnalysisSupported: true, auxiliaryRepositoriesSupported: true, }, { @@ -214,7 +208,6 @@ describe('ProfileService', () => { projectTypes: [], sequentialTestRuns: false, staticCodeAnalysis: false, - testwiseCoverageAnalysisSupported: false, auxiliaryRepositoriesSupported: true, }, { @@ -225,7 +218,6 @@ describe('ProfileService', () => { projectTypes: [ProjectType.PLAIN, ProjectType.XCODE], sequentialTestRuns: false, staticCodeAnalysis: true, - testwiseCoverageAnalysisSupported: false, auxiliaryRepositoriesSupported: true, }, { @@ -236,7 +228,6 @@ describe('ProfileService', () => { projectTypes: [], sequentialTestRuns: false, staticCodeAnalysis: false, - testwiseCoverageAnalysisSupported: false, auxiliaryRepositoriesSupported: true, }, { @@ -247,7 +238,6 @@ describe('ProfileService', () => { projectTypes: [ProjectType.PLAIN_MAVEN, ProjectType.MAVEN_MAVEN], sequentialTestRuns: true, staticCodeAnalysis: true, - testwiseCoverageAnalysisSupported: true, auxiliaryRepositoriesSupported: true, }, ], diff --git a/src/test/javascript/spec/service/programming-exercise-task.service.spec.ts b/src/test/javascript/spec/service/programming-exercise-task.service.spec.ts index a47cc762f73b..3b9b7b28e1ff 100644 --- a/src/test/javascript/spec/service/programming-exercise-task.service.spec.ts +++ b/src/test/javascript/spec/service/programming-exercise-task.service.spec.ts @@ -10,7 +10,7 @@ import { ProgrammingExerciseGradingStatistics, TestCaseStats } from 'app/entitie import { MockProvider } from 'ng-mocks'; import { MockProgrammingExerciseGradingService } from '../helpers/mocks/service/mock-programming-exercise-grading.service'; import { ProgrammingExerciseTask } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task'; -import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; +import { ProgrammingExerciseServerSideTask } from '../../../../main/webapp/app/entities/programming-exercise-task.model'; import { ProgrammingExerciseTestCase, ProgrammingExerciseTestCaseType, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; import { firstValueFrom, of } from 'rxjs'; import { provideHttpClient } from '@angular/common/http'; diff --git a/src/test/javascript/spec/service/programming-exercise.service.spec.ts b/src/test/javascript/spec/service/programming-exercise.service.spec.ts index bcc07eeecb3a..6473ce25204c 100644 --- a/src/test/javascript/spec/service/programming-exercise.service.spec.ts +++ b/src/test/javascript/spec/service/programming-exercise.service.spec.ts @@ -14,12 +14,11 @@ import { ProgrammingSubmission } from 'app/entities/programming/programming-subm import { Result } from 'app/entities/result.model'; import { AccountService } from 'app/core/auth/account.service'; import { MockAccountService } from '../helpers/mocks/service/mock-account.service'; -import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; import { Course } from 'app/entities/course.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { Submission } from 'app/entities/submission.model'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; -import { ProgrammingExerciseGitDiffEntry } from 'app/entities/hestia/programming-exercise-git-diff-entry.model'; +import { ProgrammingExerciseGitDiffReport } from '../../../../main/webapp/app/entities/programming-exercise-git-diff-report.model'; +import { ProgrammingExerciseGitDiffEntry } from '../../../../main/webapp/app/entities/programming-exercise-git-diff-entry.model'; import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; import { provideHttpClient } from '@angular/common/http'; @@ -283,24 +282,6 @@ describe('ProgrammingExercise Service', () => { })); }); - it('should make post request for structural solution entries', fakeAsync(() => { - const expected = [new ProgrammingExerciseSolutionEntry()]; - expected[0].filePath = 'src/test.java'; - service.createStructuralSolutionEntries(123).subscribe((resp) => expect(resp).toEqual(expected)); - const req = httpMock.expectOne({ method: 'POST', url: `${resourceUrl}/123/structural-solution-entries` }); - req.flush(expected); - tick(); - })); - - it('should make post request for behavioral solution entries', fakeAsync(() => { - const expected = [new ProgrammingExerciseSolutionEntry()]; - expected[0].filePath = 'src/test.java'; - service.createBehavioralSolutionEntries(123).subscribe((resp) => expect(resp).toEqual(expected)); - const req = httpMock.expectOne({ method: 'POST', url: `${resourceUrl}/123/behavioral-solution-entries` }); - req.flush(expected); - tick(); - })); - it('should make post request for import from file', fakeAsync(() => { const course = new Course(); course.id = 1; @@ -444,12 +425,7 @@ describe('ProgrammingExercise Service', () => { { uri: 'check-plagiarism', method: 'checkPlagiarism' }, { uri: 'plagiarism-result', method: 'getLatestPlagiarismResult' }, { uri: 'test-case-state', method: 'getProgrammingExerciseTestCaseState' }, - { uri: 'tasks', method: 'getTasksAndTestsExtractedFromProblemStatement' }, { uri: 'diff-report', method: 'getDiffReport' }, - { uri: 'full-testwise-coverage-report', method: 'getLatestFullTestwiseCoverageReport' }, - { uri: 'file-names', method: 'getSolutionFileNames' }, - { uri: 'exercise-hints', method: 'getCodeHintsForExercise' }, - { uri: 'test-cases', method: 'getAllTestCases' }, { uri: 'build-log-statistics', method: 'getBuildLogStatistics' }, ])('should call correct exercise endpoint', (test) => fakeAsync(() => { diff --git a/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseAssessment.spec.ts b/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseAssessment.spec.ts index 0299bd423774..fc1282c3fc36 100644 --- a/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseAssessment.spec.ts +++ b/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseAssessment.spec.ts @@ -33,7 +33,6 @@ test.describe('Programming exercise assessment', { tag: '@sequential' }, () => { assessmentDueDate = dueDate.add(20, 'seconds'); exercise = await exerciseAPIRequests.createProgrammingExercise({ course, - recordTestwiseCoverage: false, releaseDate: dayjs(), dueDate: dueDate, assessmentDate: assessmentDueDate, diff --git a/src/test/playwright/fixtures/exercise/programming/python/template.json b/src/test/playwright/fixtures/exercise/programming/python/template.json index fe3456326355..a9ffcf14c969 100644 --- a/src/test/playwright/fixtures/exercise/programming/python/template.json +++ b/src/test/playwright/fixtures/exercise/programming/python/template.json @@ -33,7 +33,6 @@ "templateParticipation": { "type": "template" }, - "testwiseCoverageEnabled": false, "title": "", "type": "programming", "buildConfig": { diff --git a/src/test/playwright/support/requests/ExerciseAPIRequests.ts b/src/test/playwright/support/requests/ExerciseAPIRequests.ts index 20d55b216fa8..e9c836fb3e1d 100644 --- a/src/test/playwright/support/requests/ExerciseAPIRequests.ts +++ b/src/test/playwright/support/requests/ExerciseAPIRequests.ts @@ -70,7 +70,6 @@ export class ExerciseAPIRequests { * - exerciseGroup: The exercise group the exercise will be added to * - scaMaxPenalty: The max percentage (0-100) static code analysis can reduce from the points * If sca should be disabled, pass null or omit this property - * - recordTestwiseCoverage: Enable testwise coverage analysis for this exercise * - releaseDate: When the programming exercise should be available * - dueDate: When the programming exercise should be due * - title: The title of the programming exercise @@ -85,7 +84,6 @@ export class ExerciseAPIRequests { course?: Course; exerciseGroup?: ExerciseGroup; scaMaxPenalty?: number | undefined; - recordTestwiseCoverage?: boolean; releaseDate?: dayjs.Dayjs; dueDate?: dayjs.Dayjs; title?: string; @@ -101,7 +99,6 @@ export class ExerciseAPIRequests { course, exerciseGroup, scaMaxPenalty = undefined, - recordTestwiseCoverage = false, releaseDate = dayjs(), dueDate = dayjs().add(1, 'day'), title = 'Programming ' + generateUUID(), @@ -147,7 +144,6 @@ export class ExerciseAPIRequests { } exercise.programmingLanguage = programmingLanguage; - exercise.buildConfig!.testwiseCoverageEnabled = recordTestwiseCoverage; exercise.mode = mode; exercise.teamAssignmentConfig = teamAssignmentConfig; diff --git a/supporting_scripts/course-scripts/quick-course-setup/manage_programming_exercise.py b/supporting_scripts/course-scripts/quick-course-setup/manage_programming_exercise.py index ab2dfc469d5d..b550fb565d36 100644 --- a/supporting_scripts/course-scripts/quick-course-setup/manage_programming_exercise.py +++ b/supporting_scripts/course-scripts/quick-course-setup/manage_programming_exercise.py @@ -29,7 +29,6 @@ def create_programming_exercise(session: Session, course_id: int, server_url: st "buildConfig": { "buildScript": "#!/usr/bin/env bash\nset -e\n\ngradle () {\n echo '⚙️ executing gradle'\n chmod +x ./gradlew\n ./gradlew clean test\n}\n\nmain () {\n gradle\n}\n\nmain \"${@}\"\n", "checkoutSolutionRepository": False, - "testwiseCoverageEnabled": False }, }