250f253d8decbd6f57be9180d7a296b08df184f5
[idea/community.git] / python / educational-python / resources / fileTemplates / internal / test_helper.py.ft
1 import sys
2
3
4 def get_file_text(path):
5     """ Returns file text by path"""
6     file_io = open(path, "r")
7     text = file_io.read()
8     file_io.close()
9     return text
10
11
12 def get_file_output(encoding="utf-8", path=sys.argv[-1], arg_string=""):
13     """
14     Returns answer file output
15     :param encoding: to decode output in python3
16     :param path: path of file to execute
17     :return: list of strings
18     """
19     import subprocess
20
21     proc = subprocess.Popen([sys.executable, path], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
22                             stderr=subprocess.STDOUT)
23     if arg_string:
24         for arg in arg_string.split("\n"):
25             proc.stdin.write(bytearray(str(arg) + "\n", encoding))
26             proc.stdin.flush()
27
28     return list(map(lambda x: str(x.decode(encoding)), proc.communicate()[0].splitlines()))
29
30
31 def test_file_importable():
32     """ Tests there is no obvious syntax errors"""
33     path = sys.argv[-1]
34     if not path.endswith(".py"):
35         import os
36
37         parent = os.path.abspath(os.path.join(path, os.pardir))
38         python_files = [f for f in os.listdir(parent) if os.path.isfile(os.path.join(parent, f)) and f.endswith(".py")]
39         for python_file in python_files:
40             if python_file == "tests.py":
41                 continue
42             check_importable_path(os.path.join(parent, python_file))
43         return
44     check_importable_path(path)
45
46
47 def check_importable_path(path):
48     """ Checks that file is importable.
49         Reports failure otherwise.
50     """
51     try:
52         import_file(path)
53     except:
54         failed("The file contains syntax errors", test_file_importable.__name__)
55         return
56     passed(test_file_importable.__name__)
57
58
59 def import_file(path):
60     """ Returns imported file """
61     if sys.version_info[0] == 2 or sys.version_info[1] < 3:
62         import imp
63
64         return imp.load_source("tmp", path)
65     elif sys.version_info[0] == 3:
66         import importlib.machinery
67
68         return importlib.machinery.SourceFileLoader("tmp", path).load_module("tmp")
69
70
71 def import_task_file():
72     """ Returns imported file.
73         Imports file from which check action was run
74     """
75     path = sys.argv[-1]
76     return import_file(path)
77
78
79 def test_is_not_empty():
80     """
81         Checks that file is not empty
82     """
83     path = sys.argv[-1]
84     file_text = get_file_text(path)
85
86     if len(file_text) > 0:
87         passed()
88     else:
89         failed("The file is empty. Please, reload the task and try again.")
90
91
92 def test_is_initial_text(error_text="You should modify the file"):
93     """
94         Checks that file was modified
95     """
96     path = sys.argv[-1]
97     text = get_initial_text(path)
98     file_text = get_file_text(path)
99
100     if file_text.strip() == text.strip():
101         failed(error_text)
102     else:
103         passed()
104
105
106 def get_initial_text(path):
107     """
108         Returns the initial task text
109     """
110     course_lib = sys.argv[-2]
111
112     import os
113     # path format is "project_root/lessonX/taskY/file.py"
114     task_index = path.rfind(os.sep, 0, path.rfind(os.sep))
115     index = path.rfind(os.sep, 0, task_index)
116     relative_path = path[index + 1:]
117     initial_file_path = os.path.join(course_lib, relative_path)
118     return get_file_text(initial_file_path)
119
120
121 def test_text_equals(text, error_text):
122     """
123         Checks that answer equals text.
124     """
125     path = sys.argv[-1]
126     file_text = get_file_text(path)
127
128     if file_text.strip() == text:
129         passed()
130     else:
131         failed(error_text)
132
133
134 def test_answer_placeholders_text_deleted(error_text="Don't just delete task text"):
135     """
136         Checks that all answer placeholders are not empty
137     """
138     windows = get_answer_placeholders()
139
140     for window in windows:
141         if len(window) == 0:
142             failed(error_text)
143             return
144     passed()
145
146
147 def set_congratulation_message(message):
148     """ Overrides default 'Congratulations!' message """
149     print("#educational_plugin CONGRATS_MESSAGE " + message)
150
151
152 def failed(message="Please, reload the task and try again.", name=None):
153     """ Reports failure """
154     if not name:
155         name = sys._getframe().f_back.f_code.co_name
156     print("#educational_plugin " + name + " FAILED + " + message)
157
158
159 def passed(name=None):
160     """ Reports success """
161     if not name:
162         name = sys._getframe().f_back.f_code.co_name
163     print("#educational_plugin " + name + " test OK")
164
165
166 def get_answer_placeholders():
167     """
168         Returns all answer placeholders text
169     """
170     prefix = "#educational_plugin_window = "
171     path = sys.argv[-1]
172     import os
173
174     file_name_without_extension = os.path.splitext(path)[0]
175     windows_path = file_name_without_extension + "_windows"
176     windows = []
177     f = open(windows_path, "r")
178     window_text = ""
179     first = True
180     for line in f.readlines():
181         if line.startswith(prefix):
182             if not first:
183                 windows.append(window_text.strip())
184             else:
185                 first = False
186             window_text = line[len(prefix):]
187         else:
188             window_text += line
189
190     if window_text:
191         windows.append(window_text.strip())
192
193     f.close()
194     return windows
195
196
197 def check_samples(samples=()):
198     """
199       Check script output for all samples. Sample is a two element list, where the first is input and
200       the second is output.
201     """
202     for sample in samples:
203         if len(sample) == 2:
204             output = get_file_output(arg_string=str(sample[0]))
205             if "\n".join(output) != sample[1]:
206                 failed(
207                     "Test from samples failed: \n \n"
208                     "Input:\n {}"
209                     "\n \n"
210                     "Expected:\n {}"
211                     "\n \n"
212                     "Your result:\n {}".format(sample[0], 
213                                                         sample[1], 
214                                                         "\n".join(output)))
215                 return
216         set_congratulation_message("All test from samples passed. Now we are checking your solution on Stepic server.")
217
218     passed()
219
220
221 def run_common_tests(error_text="Please, reload file and try again"):
222     test_is_initial_text()
223     test_is_not_empty()
224     test_answer_placeholders_text_deleted()
225     test_file_importable()