1 package com.jetbrains.edu.learning.checker;
3 import com.intellij.execution.process.CapturingProcessHandler;
4 import com.intellij.execution.process.ProcessOutput;
5 import com.intellij.ide.projectView.ProjectView;
6 import com.intellij.openapi.application.ApplicationManager;
7 import com.intellij.openapi.diagnostic.Logger;
8 import com.intellij.openapi.editor.Document;
9 import com.intellij.openapi.fileEditor.FileDocumentManager;
10 import com.intellij.openapi.progress.ProgressIndicator;
11 import com.intellij.openapi.project.Project;
12 import com.intellij.openapi.ui.MessageType;
13 import com.intellij.openapi.util.Pair;
14 import com.intellij.openapi.util.Ref;
15 import com.intellij.openapi.util.TextRange;
16 import com.intellij.openapi.vfs.VirtualFile;
17 import com.jetbrains.edu.learning.*;
18 import com.jetbrains.edu.learning.actions.StudyAfterCheckAction;
19 import com.jetbrains.edu.learning.core.EduNames;
20 import com.jetbrains.edu.learning.core.EduUtils;
21 import com.jetbrains.edu.learning.courseFormat.*;
22 import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector;
23 import com.jetbrains.edu.learning.stepic.EduStepicConnector;
24 import org.jetbrains.annotations.NotNull;
25 import org.jetbrains.annotations.Nullable;
29 public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroundable {
31 private static final Logger LOG = Logger.getInstance(StudyCheckTask.class);
32 private final Project myProject;
33 protected final StudyState myStudyState;
34 protected final Task myTask;
35 protected final VirtualFile myTaskDir;
36 protected final StudyTaskManager myTaskManger;
37 private final StudyStatus myStatusBeforeCheck;
38 private final Ref<Boolean> myCheckInProcess;
39 private final Process myTestProcess;
40 private final String myCommandLine;
41 private static final String FAILED_CHECK_LAUNCH = "Failed to launch checking";
43 public StudyCheckTask(Project project, StudyState studyState, Ref<Boolean> checkInProcess, Process testProcess, String commandLine) {
44 super(project, "Checking Task");
46 myStudyState = studyState;
47 myCheckInProcess = checkInProcess;
48 myTestProcess = testProcess;
49 myCommandLine = commandLine;
50 myTask = studyState.getTask();
51 myTaskDir = studyState.getTaskDir();
52 myTaskManger = StudyTaskManager.getInstance(myProject);
53 myStatusBeforeCheck = myTask.getStatus();
57 public void onSuccess() {
58 StudyUtils.updateToolWindows(myProject);
59 StudyCheckUtils.drawAllPlaceholders(myProject, myTask, myTaskDir);
60 ProjectView.getInstance(myProject).refresh();
64 protected void clearState() {
65 EduUtils.deleteWindowDescriptions(myTask, myTaskDir);
66 myCheckInProcess.set(false);
70 public void onCancel() {
71 myTask.setStatus(myStatusBeforeCheck);
76 public void run(@NotNull ProgressIndicator indicator) {
77 final Course course = StudyTaskManager.getInstance(myProject).getCourse();
79 if (course.isAdaptive()) {
80 checkForAdaptiveCourse(indicator);
83 checkForEduCourse(indicator);
88 private void checkForEduCourse(@NotNull ProgressIndicator indicator) {
89 final StudyTestsOutputParser.TestsOutput testsOutput = getTestOutput(indicator);
91 if (testsOutput != null) {
92 if (testsOutput.isSuccess()) {
93 onTaskSolved(testsOutput.getMessage());
96 onTaskFailed(testsOutput.getMessage());
98 runAfterTaskCheckedActions();
99 final Course course = StudyTaskManager.getInstance(myProject).getCourse();
100 if (course != null && EduNames.STUDY.equals(course.getCourseMode())) {
101 EduStepicConnector.postAttempt(myTask, testsOutput.isSuccess(), myProject);
107 private StudyTestsOutputParser.TestsOutput getTestOutput(@NotNull ProgressIndicator indicator) {
108 final CapturingProcessHandler handler = new CapturingProcessHandler(myTestProcess, null, myCommandLine);
109 final ProcessOutput output = handler.runProcessWithProgressIndicator(indicator);
110 if (indicator.isCanceled()) {
111 ApplicationManager.getApplication().invokeLater(
112 () -> StudyCheckUtils.showTestResultPopUp("Check cancelled", MessageType.WARNING.getPopupBackground(), myProject));
115 final Course course = StudyTaskManager.getInstance(myProject).getCourse();
116 if (course != null) {
117 final StudyTestsOutputParser.TestsOutput testsOutput = StudyTestsOutputParser.getTestsOutput(output, course.isAdaptive());
118 String stderr = output.getStderr();
119 if (!stderr.isEmpty() && output.getStdout().isEmpty()) {
120 //log error output of tests
121 LOG.info("#educational " + stderr);
122 return new StudyTestsOutputParser.TestsOutput(false, stderr);
129 private void checkForAdaptiveCourse(ProgressIndicator indicator) {
130 final StudyTestsOutputParser.TestsOutput testOutput = getTestOutput(indicator);
131 if (testOutput != null) {
132 // As tests in adaptive courses are created from
133 // samples and stored in task, to disable it we should ignore local testing results
134 if (StudyTaskManager.getInstance(myProject).isEnableTestingFromSamples() && !testOutput.isSuccess()) {
135 onTaskFailed(testOutput.getMessage());
138 final Pair<Boolean, String> pair = EduAdaptiveStepicConnector.checkTask(myProject, myTask);
139 if (pair != null && !(!pair.getFirst() && pair.getSecond().isEmpty())) {
140 if (pair.getFirst()) {
141 onTaskSolved("Congratulations! Remote tests passed.");
142 if (myStatusBeforeCheck != StudyStatus.Solved) {
143 EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, 2, indicator);
147 final String checkMessage = pair.getSecond();
148 onTaskFailed(checkMessage);
150 runAfterTaskCheckedActions();
153 ApplicationManager.getApplication().invokeLater(() -> StudyCheckUtils.showTestResultPopUp(FAILED_CHECK_LAUNCH,
155 .getPopupBackground(),
162 protected void onTaskFailed(String message) {
163 final Course course = StudyTaskManager.getInstance(myProject).getCourse();
164 myTask.setStatus(StudyStatus.Failed);
165 if (course != null) {
166 if (course.isAdaptive()) {
167 ApplicationManager.getApplication().invokeLater(
169 StudyCheckUtils.showTestResultPopUp("Failed", MessageType.ERROR.getPopupBackground(), myProject);
170 StudyCheckUtils.showTestResultsToolWindow(myProject, message, false);
174 ApplicationManager.getApplication()
175 .invokeLater(() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), myProject));
180 protected void onTaskSolved(String message) {
181 final Course course = StudyTaskManager.getInstance(myProject).getCourse();
182 myTask.setStatus(StudyStatus.Solved);
183 if (course != null) {
184 if (course.isAdaptive()) {
185 ApplicationManager.getApplication().invokeLater(
187 StudyCheckUtils.showTestResultPopUp("Congratulations!", MessageType.INFO.getPopupBackground(), myProject);
188 StudyCheckUtils.showTestResultsToolWindow(myProject, message, true);
192 boolean hasMoreSubtasks = myTask.hasSubtasks() && myTask.getActiveSubtaskIndex() != myTask.getSubtaskNum() - 1;
193 int visibleSubtaskIndex = myTask.getActiveSubtaskIndex() + 1;
194 ApplicationManager.getApplication().invokeLater(() -> {
195 String resultMessage = !hasMoreSubtasks ? message : "Subtask " + visibleSubtaskIndex + "/" + myTask.getSubtaskNum() + " solved";
196 StudyCheckUtils.showTestResultPopUp(resultMessage, MessageType.INFO.getPopupBackground(), myProject);
197 if (hasMoreSubtasks) {
198 int nextSubtaskIndex = myTask.getActiveSubtaskIndex() + 1;
199 StudySubtaskUtils.switchStep(myProject, myTask, nextSubtaskIndex);
200 rememberAnswers(nextSubtaskIndex);
207 private void rememberAnswers(int nextSubtaskIndex) {
208 VirtualFile taskDir = myTask.getTaskDir(myProject);
209 if (taskDir == null) {
212 VirtualFile srcDir = taskDir.findChild(EduNames.SRC);
213 if (srcDir != null) {
216 for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
217 TaskFile taskFile = entry.getValue();
218 VirtualFile virtualFile = taskDir.findChild(entry.getKey());
219 if (virtualFile == null) {
222 Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
223 if (document == null) {
226 for (AnswerPlaceholder placeholder : taskFile.getActivePlaceholders()) {
227 if (placeholder.getSubtaskInfos().containsKey(nextSubtaskIndex - 1)) {
228 int offset = placeholder.getOffset();
229 String answer = document.getText(TextRange.create(offset, offset + placeholder.getRealLength()));
230 placeholder.getSubtaskInfos().get(nextSubtaskIndex - 1).setAnswer(answer);
236 private void runAfterTaskCheckedActions() {
237 StudyPluginConfigurator configurator = StudyUtils.getConfigurator(myProject);
238 if (configurator != null) {
239 StudyAfterCheckAction[] checkActions = configurator.getAfterCheckActions();
240 if (checkActions != null) {
241 for (StudyAfterCheckAction action : checkActions) {
242 action.run(myProject, myTask, myStatusBeforeCheck);
247 LOG.warn("No configurator is provided for the plugin");