Add tests from samples provided for step and show test result in separate tool window

This commit is contained in:
Valentina Kiryushkina
2016-05-02 14:31:38 +03:00
parent 2d880f726a
commit 2c958c2b57
15 changed files with 303 additions and 160 deletions
@@ -85,6 +85,7 @@
<toolWindow id="Task Description" anchor="right" factoryClass="com.jetbrains.edu.learning.ui.StudyToolWindowFactory" conditionClass="com.jetbrains.edu.learning.ui.StudyCondition"/>
<toolWindow id="Course Progress" anchor="left" factoryClass="com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory" conditionClass="com.jetbrains.edu.learning.ui.StudyCondition"/>
<toolWindow id="Test Results" anchor="bottom" factoryClass="com.jetbrains.edu.learning.ui.StudyTestResultsToolWindowFactory" conditionClass="com.jetbrains.edu.learning.ui.StudyCondition"/>
<fileEditorProvider implementation="com.jetbrains.edu.learning.editor.StudyFileEditorProvider"/>
<treeStructureProvider implementation="com.jetbrains.edu.learning.projectView.StudyTreeStructureProvider"/>
<highlightErrorFilter implementation="com.jetbrains.edu.learning.editor.StudyHighlightErrorFilter"/>
@@ -69,6 +69,7 @@ public abstract class StudyBasePluginConfigurator implements StudyPluginConfigur
Task task = getTask(file);
setTaskText(task, file.getParent());
}
toolWindow.setBottomComponent(null);
}
@Nullable
@@ -25,6 +25,7 @@ import com.jetbrains.edu.learning.courseFormat.Task;
import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroundable {
@@ -75,21 +76,36 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
public void run(@NotNull ProgressIndicator indicator) {
final Course course = StudyTaskManager.getInstance(myProject).getCourse();
if (course != null && course.isAdaptive()) {
checkAdaptiveCourse(indicator);
checkForAdaptiveCourse(indicator);
}
else {
if (checkCourse(indicator)) return;
checkForEduCourse(indicator);
}
runAfterTaskSolvedActions();
}
private boolean checkCourse(@NotNull ProgressIndicator indicator) {
private void checkForEduCourse(@NotNull ProgressIndicator indicator) {
final StudyTestsOutputParser.TestsOutput testsOutput = getTestOutput(indicator);
postAttemptToStepic(testsOutput);
if (testsOutput != null) {
if (testsOutput.isSuccess()) {
onTaskSolved(testsOutput.getMessage());
}
else {
onTaskFailed(testsOutput.getMessage());
}
runAfterTaskCheckedActions();
}
}
@Nullable
private StudyTestsOutputParser.TestsOutput getTestOutput(@NotNull ProgressIndicator indicator) {
final CapturingProcessHandler handler = new CapturingProcessHandler(myTestProcess, null, myCommandLine);
final ProcessOutput output = handler.runProcessWithProgressIndicator(indicator);
if (indicator.isCanceled()) {
ApplicationManager.getApplication().invokeLater(
() -> StudyCheckUtils.showTestResultPopUp("Check cancelled", MessageType.WARNING.getPopupBackground(), myProject));
return true;
}
@@ -102,64 +118,64 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
myProject));
//log error output of tests
LOG.info("#educational " + stderr);
return true;
return null;
}
postAttemptToStepic(testsOutput);
if (testsOutput.isSuccess()) {
onTaskSolved(testsOutput.getMessage());
}
else {
onTaskFailed(testsOutput.getMessage());
}
return false;
return testsOutput;
}
private void checkAdaptiveCourse(ProgressIndicator indicator) {
private void checkForAdaptiveCourse(ProgressIndicator indicator) {
ProgressManager.getInstance().runProcessWithProgressAsynchronously(new Backgroundable(myProject, "Checking Task") {
@Override
public void run(@NotNull ProgressIndicator indicator) {
final Pair<Boolean, String> pair = EduAdaptiveStepicConnector.checkTask(myProject, myTask);
if (pair != null) {
final String checkMessage = pair.getSecond();
if (pair.getFirst()) {
onTaskSolved(checkMessage);
final StudyTestsOutputParser.TestsOutput testOutput = getTestOutput(indicator);
if (testOutput != null) {
if (testOutput.isSuccess()) {
final Pair<Boolean, String> pair = EduAdaptiveStepicConnector.checkTask(myProject, myTask);
if (pair != null && !pair.getSecond().isEmpty()) {
final String checkMessage = pair.getSecond();
if (pair.getFirst()) {
onTaskSolved(checkMessage);
}
else {
onTaskFailed(checkMessage);
}
runAfterTaskCheckedActions();
}
else {
ApplicationManager.getApplication().invokeLater(() ->
StudyCheckUtils.showTestResultPopUp("Failed to launch checking",
MessageType.WARNING
.getPopupBackground(),
myProject));
}
}
else {
onTaskFailed(checkMessage);
onTaskFailed(testOutput.getMessage());
}
}
else {
ApplicationManager.getApplication().invokeLater(() ->
StudyCheckUtils.showTestResultPopUp("Failed to launch checking",
MessageType.WARNING.getPopupBackground(),
myProject));
}
}
}, indicator);
}
protected void onTaskFailed( String message) {
protected void onTaskFailed(String message) {
myTaskManger.setStatus(myTask, StudyStatus.Failed);
ApplicationManager.getApplication().invokeLater(
() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), myProject));
() -> StudyCheckUtils.showTestResults(myProject, message));
}
protected void onTaskSolved(String message) {
myTaskManger.setStatus(myTask, StudyStatus.Solved);
ApplicationManager.getApplication().invokeLater(
() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.INFO.getPopupBackground(), myProject));
() -> StudyCheckUtils.showTestResults(myProject, message));
}
private void runAfterTaskSolvedActions() {
private void runAfterTaskCheckedActions() {
StudyPluginConfigurator configurator = StudyUtils.getConfigurator(myProject);
if (configurator != null) {
StudyAfterCheckAction[] checkActions = configurator.getAfterCheckActions();
if (checkActions != null) {
for (StudyAfterCheckAction action: checkActions) {
for (StudyAfterCheckAction action : checkActions) {
action.run(myProject, myTask, myStatusBeforeCheck);
}
}
@@ -15,23 +15,25 @@ import com.intellij.openapi.ui.popup.BalloonBuilder;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.*;
import com.intellij.openapi.wm.ex.StatusBarEx;
import com.intellij.openapi.wm.ex.WindowManagerEx;
import com.intellij.ui.content.Content;
import com.jetbrains.edu.learning.StudyState;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduDocumentListener;
import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
import com.jetbrains.edu.learning.courseFormat.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.StudyState;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.editor.StudyEditor;
import com.jetbrains.edu.learning.navigation.StudyNavigator;
import com.jetbrains.edu.learning.ui.StudyTestResultsToolWindow;
import com.jetbrains.edu.learning.ui.StudyTestResultsToolWindowFactory;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.util.List;
@@ -61,9 +63,9 @@ public class StudyCheckUtils {
}
public static void navigateToFailedPlaceholder(@NotNull final StudyState studyState,
@NotNull final Task task,
@NotNull final VirtualFile taskDir,
@NotNull final Project project) {
@NotNull final Task task,
@NotNull final VirtualFile taskDir,
@NotNull final Project project) {
TaskFile selectedTaskFile = studyState.getTaskFile();
Editor editor = studyState.getEditor();
TaskFile taskFileToNavigate = selectedTaskFile;
@@ -109,10 +111,10 @@ public class StudyCheckUtils {
public static void runSmartTestProcess(@NotNull final VirtualFile taskDir,
@NotNull final StudyTestRunner testRunner,
final String taskFileName,
@NotNull final TaskFile taskFile,
@NotNull final Project project) {
@NotNull final StudyTestRunner testRunner,
final String taskFileName,
@NotNull final TaskFile taskFile,
@NotNull final Project project) {
final TaskFile answerTaskFile = new TaskFile();
answerTaskFile.name = taskFileName;
final VirtualFile virtualFile = taskDir.findChild(taskFileName);
@@ -135,11 +137,10 @@ public class StudyCheckUtils {
}
private static VirtualFile getCopyWithAnswers(@NotNull final VirtualFile taskDir,
@NotNull final VirtualFile file,
@NotNull final TaskFile source,
@NotNull final TaskFile target) {
@NotNull final VirtualFile file,
@NotNull final TaskFile source,
@NotNull final TaskFile target) {
VirtualFile copy = null;
try {
@@ -193,4 +194,22 @@ public class StudyCheckUtils {
EduUtils.flushWindows(taskFile, virtualFile, true);
}
}
public static void showTestResults(@NotNull final Project project, @NotNull final String message) {
final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
ToolWindow window = toolWindowManager.getToolWindow("Test Results");
if (window == null) {
toolWindowManager.registerToolWindow("Test Results", true, ToolWindowAnchor.BOTTOM);
window = toolWindowManager.getToolWindow("Test Results");
new StudyTestResultsToolWindowFactory().createToolWindowContent(project, window);
}
final Content[] contents = window.getContentManager().getContents();
for (Content content : contents) {
final JComponent component = content.getComponent();
if (component instanceof StudyTestResultsToolWindow) {
((StudyTestResultsToolWindow)component).setText(message);
component.setVisible(true);
}
}
}
}
@@ -4,7 +4,7 @@ import com.intellij.execution.process.ProcessOutput;
import org.jetbrains.annotations.NotNull;
public class StudyTestsOutputParser {
private static final String ourStudyPrefix = "#educational_plugin";
private static final String STUDY_PREFIX = "#educational_plugin";
public static final String TEST_OK = "test OK";
private static final String TEST_FAILED = "FAILED + ";
private static final String CONGRATS_MESSAGE = "CONGRATS_MESSAGE ";
@@ -32,7 +32,7 @@ public class StudyTestsOutputParser {
public static TestsOutput getTestsOutput(@NotNull final ProcessOutput processOutput) {
String congratulations = CONGRATULATIONS;
for (String line : processOutput.getStdoutLines()) {
if (line.startsWith(ourStudyPrefix)) {
if (line.startsWith(STUDY_PREFIX)) {
if (line.contains(TEST_OK)) {
continue;
}
@@ -4,13 +4,13 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.courseFormat.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@@ -23,6 +23,7 @@ public class StudyGenerator {
private StudyGenerator() {
}
private static final Logger LOG = Logger.getInstance(StudyGenerator.class.getName());
/**
@@ -107,34 +108,32 @@ public class StudyGenerator {
public static void createCourse(@NotNull final Course course, @NotNull final VirtualFile baseDir, @NotNull final File resourceRoot,
@NotNull final Project project) {
try {
final List<Lesson> lessons = course.getLessons();
for (int i = 1; i <= lessons.size(); i++) {
Lesson lesson = lessons.get(i - 1);
lesson.setIndex(i);
createLesson(lesson, baseDir, resourceRoot, project);
}
baseDir.createChildDirectory(project, EduNames.SANDBOX_DIR);
File[] files = resourceRoot.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS);
}
});
for (File file : files) {
File dir = new File(baseDir.getPath(), file.getName());
if (file.isDirectory()) {
FileUtil.copyDir(file, dir);
continue;
}
try {
final List<Lesson> lessons = course.getLessons();
for (int i = 1; i <= lessons.size(); i++) {
Lesson lesson = lessons.get(i - 1);
lesson.setIndex(i);
createLesson(lesson, baseDir, resourceRoot, project);
}
baseDir.createChildDirectory(project, EduNames.SANDBOX_DIR);
File[] files = resourceRoot.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS);
}
});
for (File file : files) {
File dir = new File(baseDir.getPath(), file.getName());
if (file.isDirectory()) {
FileUtil.copyDir(file, dir);
continue;
}
FileUtil.copy(file, dir);
}
}
catch (IOException e) {
LOG.error(e);
}
FileUtil.copy(file, dir);
}
}
catch (IOException e) {
LOG.error(e);
}
}
}
@@ -9,6 +9,7 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.jetbrains.edu.learning.StudyTaskManager;
@@ -42,6 +43,7 @@ import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static com.jetbrains.edu.learning.stepic.EduStepicConnector.getHttpClient;
@@ -57,6 +59,8 @@ public class EduAdaptiveStepicConnector {
private static final String RECOMMENDATION_REACTIONS_URL = "recommendation-reactions";
private static final String ATTEMPTS_URL = "attempts";
private static final String SUBMISSION_URL = "submissions";
private static final String PYTHON2 = "python2";
private static final String PYTHON3 = "python3";
@Nullable
public static Task getNextRecommendation(@NotNull final Project project, @NotNull Course course) {
@@ -88,9 +92,11 @@ public class EduAdaptiveStepicConnector {
for (int stepId : realLesson.steps) {
final StepicWrappers.Step step = EduStepicConnector.getStep(stepId);
if (step.name.equals("code")) {
return getTaskFromStep(realLesson.getName(), stepId, step);
return getTaskFromStep(project, stepId, step, realLesson.getName());
}
}
LOG.warn("Got a lesson without code part as a recommendation");
}
}
else {
@@ -106,8 +112,8 @@ public class EduAdaptiveStepicConnector {
return null;
}
public static boolean postRecommendationReaction(@NotNull final Project project, @NotNull final String lessonId,
@NotNull final String user,int reaction) {
public static boolean postRecommendationReaction(@NotNull final Project project, @NotNull final String lessonId,
@NotNull final String user, int reaction) {
final HttpPost post = new HttpPost(STEPIC_API_URL + RECOMMENDATION_REACTIONS_URL);
final String json = new Gson()
@@ -132,7 +138,7 @@ public class EduAdaptiveStepicConnector {
// TODO: get user from settings
final StepicUser user = StudyTaskManager.getInstance(project).getUser();
if (user != null &&
postRecommendationReaction(project, String.valueOf(editor.getTaskFile().getTask().getLesson().id),
postRecommendationReaction(project, String.valueOf(editor.getTaskFile().getTask().getLesson().id),
String.valueOf(user.id), reaction)) {
final Task task = getNextRecommendation(project, course);
@@ -188,16 +194,37 @@ public class EduAdaptiveStepicConnector {
}
@NotNull
private static Task getTaskFromStep(@NotNull String name, int lessonID, @NotNull final StepicWrappers.Step step) {
private static Task getTaskFromStep(Project project,
int lessonID,
@NotNull final StepicWrappers.Step step, @NotNull String name) {
final Task task = new Task();
task.setName(name);
task.setStepicId(lessonID);
task.setText(step.text);
if (step.options.samples != null) {
final StringBuilder builder = new StringBuilder();
for (List<String> sample : step.options.samples) {
if (sample.size() == 2) {
builder.append("<b>Sample Input:</b><br>");
builder.append(sample.get(0));
builder.append("<br>");
builder.append("<b>Sample Output:</b><br>");
builder.append(sample.get(1));
builder.append("<br><br>");
}
}
task.setText(task.getText() + "<br>" + builder.toString());
}
if (step.options.test != null) {
for (StepicWrappers.TestFileWrapper wrapper : step.options.test) {
task.addTestsTexts(wrapper.name, wrapper.text);
}
}
else {
if (step.options.samples != null) {
createTestFileFromSamples(project, task, step.options.samples);
}
}
task.taskFiles = new HashMap<String, TaskFile>(); // TODO: it looks like we don't need taskFiles as map anymore
if (step.options.files != null) {
@@ -218,37 +245,39 @@ public class EduAdaptiveStepicConnector {
public static Pair<Boolean, String> checkTask(@NotNull final Project project, @NotNull final Task task) {
try {
final int attemptId = getAttemptId(project, task, ATTEMPTS_URL);
final Editor editor = StudyUtils.getSelectedEditor(project);
String language = getLanguageString(task, project);
if (editor != null && language != null) {
final StepicWrappers.SubmissionToPostWrapper submissionToPostWrapper =
new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId), "python3", editor.getDocument().getText());
final HttpPost httpPost = new HttpPost(STEPIC_API_URL + SUBMISSION_URL);
httpPost.setEntity(new StringEntity(new Gson().toJson(submissionToPostWrapper)));
final CloseableHttpClient client = getHttpClient(project);
setHeaders(httpPost, CONTENT_TYPE_APPL_JSON);
final CloseableHttpResponse execute = client.execute(httpPost);
StepicWrappers.ResultSubmissionWrapper wrapper =
new Gson().fromJson(EntityUtils.toString(execute.getEntity()), StepicWrappers.ResultSubmissionWrapper.class);
if (attemptId != -1) {
final Editor editor = StudyUtils.getSelectedEditor(project);
String language = getLanguageString(task, project);
if (editor != null && language != null) {
final StepicWrappers.SubmissionToPostWrapper submissionToPostWrapper =
new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId), language, editor.getDocument().getText());
final HttpPost httpPost = new HttpPost(STEPIC_API_URL + SUBMISSION_URL);
httpPost.setEntity(new StringEntity(new Gson().toJson(submissionToPostWrapper)));
final CloseableHttpClient client = getHttpClient(project);
setHeaders(httpPost, CONTENT_TYPE_APPL_JSON);
final CloseableHttpResponse execute = client.execute(httpPost);
StepicWrappers.ResultSubmissionWrapper wrapper =
new Gson().fromJson(EntityUtils.toString(execute.getEntity()), StepicWrappers.ResultSubmissionWrapper.class);
final StepicUser user = StudyTaskManager.getInstance(project).getUser();
if (user != null) {
final int id = user.getId();
while (wrapper.submissions.length == 1 && wrapper.submissions[0].status.equals("evaluation")) {
TimeUnit.MILLISECONDS.sleep(500);
final URI submissionURI = new URIBuilder(STEPIC_API_URL + SUBMISSION_URL)
.addParameter("attempt", String.valueOf(attemptId))
.addParameter("order", "desc")
.addParameter("user", String.valueOf(id))
.build();
final HttpGet httpGet = new HttpGet(submissionURI);
setHeaders(httpGet, CONTENT_TYPE_APPL_JSON);
final CloseableHttpResponse httpResponse = client.execute(httpGet);
wrapper = new Gson().fromJson(EntityUtils.toString(httpResponse.getEntity()), StepicWrappers.ResultSubmissionWrapper.class);
}
if (wrapper.submissions.length == 1) {
final boolean isSolved = !wrapper.submissions[0].status.equals("wrong");
return Pair.create(isSolved, wrapper.submissions[0].hint);
final StepicUser user = StudyTaskManager.getInstance(project).getUser();
if (user != null) {
final int id = user.getId();
while (wrapper.submissions.length == 1 && wrapper.submissions[0].status.equals("evaluation")) {
TimeUnit.MILLISECONDS.sleep(500);
final URI submissionURI = new URIBuilder(STEPIC_API_URL + SUBMISSION_URL)
.addParameter("attempt", String.valueOf(attemptId))
.addParameter("order", "desc")
.addParameter("user", String.valueOf(id))
.build();
final HttpGet httpGet = new HttpGet(submissionURI);
setHeaders(httpGet, CONTENT_TYPE_APPL_JSON);
final CloseableHttpResponse httpResponse = client.execute(httpGet);
wrapper = new Gson().fromJson(EntityUtils.toString(httpResponse.getEntity()), StepicWrappers.ResultSubmissionWrapper.class);
}
if (wrapper.submissions.length == 1) {
final boolean isSolved = !wrapper.submissions[0].status.equals("wrong");
return Pair.create(isSolved, wrapper.submissions[0].hint);
}
}
}
}
@@ -272,11 +301,12 @@ public class EduAdaptiveStepicConnector {
private static String getLanguageString(@NotNull Task task, @NotNull Project project) {
final Sdk sdk = StudyUtils.findSdk(task, project);
if (sdk != null) {
if (sdk.getVersionString() != null && sdk.getVersionString().startsWith("3")) {
return "python3";
}
else {
return "python2";
final String versionString = sdk.getVersionString();
if (versionString != null ) {
final List<String> versionStringParts = StringUtil.split(versionString, " ");
if (versionStringParts.size() == 2) {
return versionStringParts.get(1).startsWith("2") ? PYTHON2 : PYTHON3;
}
}
}
else {
@@ -296,6 +326,15 @@ public class EduAdaptiveStepicConnector {
final CloseableHttpResponse httpResponse = client.execute(post);
final StepicWrappers.AttemptContainer container =
new Gson().fromJson(EntityUtils.toString(httpResponse.getEntity()), StepicWrappers.AttemptContainer.class);
return container.attempts.get(0).id;
return (container.attempts != null && !container.attempts.isEmpty()) ? container.attempts.get(0).id : -1;
}
private static void createTestFileFromSamples(@NotNull final Project project,
@NotNull final Task task,
@NotNull final List<List<String>> samples) {
String testText = "from test_helper import check_samples\n\n" +
"if __name__ == '__main__':\n" +
" check_samples(samples=" + new GsonBuilder().create().toJson(samples) + ")";
task.addTestsTexts("tests.py", testText);
}
}
@@ -47,6 +47,8 @@ public class StepicWrappers {
@Expose String title;
@Expose List<TaskFile> files;
@Expose String text;
@Expose List<List<String>> samples;
public static StepOptions fromTask(final Project project, @NotNull final Task task) {
final StepOptions source = new StepOptions();
@@ -35,7 +35,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
class StudyBrowserWindow extends JFrame {
public class StudyBrowserWindow extends JFrame {
private static final Logger LOG = Logger.getInstance(StudyToolWindow.class);
private static final String EVENT_TYPE_CLICK = "click";
private JFXPanel myPanel;
@@ -0,0 +1,20 @@
package com.jetbrains.edu.learning.ui
import com.intellij.openapi.ui.SimpleToolWindowPanel
import java.awt.BorderLayout
import javax.swing.JPanel
class StudyTestResultsToolWindow: SimpleToolWindowPanel(false) {
private val studyBrowserWindow = StudyBrowserWindow(false, false)
fun init() {
val panel = JPanel(BorderLayout())
studyBrowserWindow.loadContent("", null)
panel.add(studyBrowserWindow.panel, BorderLayout.CENTER)
setContent(panel)
}
fun setText(text: String) {
studyBrowserWindow.loadContent(text, null)
}
}
@@ -0,0 +1,18 @@
package com.jetbrains.edu.learning.ui
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.ToolWindow
class StudyTestResultsToolWindowFactory: StudyToolWindowFactory() {
override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
val testResultsToolWindow = StudyTestResultsToolWindow()
testResultsToolWindow.init()
val contentManager = toolWindow.contentManager
val content = contentManager.factory.createContent(testResultsToolWindow, null, false)
contentManager.addContent(content)
}
}
@@ -219,5 +219,5 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D
}
}
public abstract void setTaskText(String text) ;
public abstract void setTaskText(String text);
}
@@ -7,9 +7,9 @@ import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowFactory;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentManager;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.StudyProjectComponent;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.courseFormat.Course;
import icons.InteractiveLearningIcons;
import org.jetbrains.annotations.NotNull;
@@ -23,7 +23,6 @@ public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware {
StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
final Course course = taskManager.getCourse();
if (course != null) {
final StudyToolWindow studyToolWindow;
if (StudyProjectComponent.getInstance(project).useJavaFx()) {
studyToolWindow = new StudyJavaFxToolWindow();
@@ -9,7 +9,7 @@ def get_file_text(path):
return text
def get_file_output(encoding="utf-8", path=sys.argv[-1]):
def get_file_output(encoding="utf-8", path=sys.argv[-1], arg_string=""):
"""
Returns answer file output
:param encoding: to decode output in python3
@@ -18,7 +18,13 @@ def get_file_output(encoding="utf-8", path=sys.argv[-1]):
"""
import subprocess
proc = subprocess.Popen([sys.executable, path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
proc = subprocess.Popen([sys.executable, path], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
if arg_string:
for arg in arg_string.split("\n"):
proc.stdin.write(bytearray(str(arg) + "\n", encoding))
proc.stdin.flush()
return list(map(lambda x: str(x.decode(encoding)), proc.communicate()[0].splitlines()))
@@ -31,7 +37,8 @@ def test_file_importable():
parent = os.path.abspath(os.path.join(path, os.pardir))
python_files = [f for f in os.listdir(parent) if os.path.isfile(os.path.join(parent, f)) and f.endswith(".py")]
for python_file in python_files:
if python_file == "tests.py": continue
if python_file == "tests.py":
continue
check_importable_path(os.path.join(parent, python_file))
return
check_importable_path(path)
@@ -187,7 +194,25 @@ def get_answer_placeholders():
return windows
def run_common_tests(error_text="Please, reload file and try again"):
def check_samples(samples=()):
"""
Check script output for all samples. Sample is a two element list, where the first is input and
the second is output.
"""
for sample in samples:
if len(sample) == 2:
output = get_file_output(arg_string=str(sample[0]))
if "\n".join(output) != sample[1]:
failed(
"<b>Input</b>:<br> {} <br><br><b>Expected:</b><br> {} <br><br> <b>Your result</b>:<br> {}".format(
sample[0].replace("\n", "<br>"), sample[1].replace("\n", "<br>"), "\n".join(output)))
return
set_congratulation_message("All test from samples passed. Now we are checking your solution on Stepic server.")
passed()
def run_common_tests():
test_is_initial_text()
test_is_not_empty()
test_answer_placeholders_text_deleted()
@@ -7,7 +7,6 @@ import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFocusManager;
import com.jetbrains.edu.learning.actions.StudyCheckAction;
@@ -15,10 +14,12 @@ import com.jetbrains.edu.learning.actions.StudyRunAction;
import com.jetbrains.edu.learning.checker.StudyCheckTask;
import com.jetbrains.edu.learning.checker.StudyCheckUtils;
import com.jetbrains.edu.learning.checker.StudyTestRunner;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.StudyStatus;
import com.jetbrains.edu.learning.courseFormat.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.editor.StudyEditor;
import com.jetbrains.edu.learning.ui.StudyToolWindow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -40,8 +41,8 @@ public class PyStudyCheckAction extends StudyCheckAction {
}
if (StudyCheckUtils.hasBackgroundProcesses(project)) return;
if (!runTask(project)) return;
final Course course = StudyTaskManager.getInstance(project).getCourse();
if (course != null && !course.isAdaptive() && !runTask(project)) return;
final Task task = studyState.getTask();
final VirtualFile taskDir = studyState.getTaskDir();
@@ -90,34 +91,37 @@ public class PyStudyCheckAction extends StudyCheckAction {
final Process testProcess,
final String commandLine) {
return new StudyCheckTask(project, studyState, myCheckInProgress, testProcess, commandLine) {
@Override
protected void onTaskFailed(String message) {
ApplicationManager.getApplication().invokeLater(() -> {
if (myTaskDir == null) return;
myTaskManger.setStatus(myTask, StudyStatus.Failed);
for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
final String name = entry.getKey();
final TaskFile taskFile = entry.getValue();
if (taskFile.getAnswerPlaceholders().size() < 2) {
myTaskManger.setStatus(taskFile, StudyStatus.Failed);
continue;
}
CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> {
StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project);
}));
}
StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), project);
StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project);
});
@Override
protected void onTaskFailed(String message) {
ApplicationManager.getApplication().invokeLater(() -> {
if (myTaskDir == null) return;
myTaskManger.setStatus(myTask, StudyStatus.Failed);
for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
final String name = entry.getKey();
final TaskFile taskFile = entry.getValue();
if (taskFile.getAnswerPlaceholders().size() < 2) {
myTaskManger.setStatus(taskFile, StudyStatus.Failed);
continue;
}
};
CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> {
StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project);
}));
}
final StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project);
if (toolWindow != null) {
StudyCheckUtils.showTestResults(project, message);
StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project);
}
});
}
};
}
@Nullable
private static VirtualFile getTaskVirtualFile(@NotNull final StudyState studyState,
@NotNull final Task task,
@NotNull final VirtualFile taskDir) {
@NotNull final Task task,
@NotNull final VirtualFile taskDir) {
VirtualFile taskVirtualFile = studyState.getVirtualFile();
for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
String name = entry.getKey();
@@ -131,7 +135,7 @@ public class PyStudyCheckAction extends StudyCheckAction {
}
return taskVirtualFile;
}
@NotNull
@Override
public String getActionId() {