forked from datacamp/pythonwhat
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTest.py
More file actions
297 lines (234 loc) · 9.45 KB
/
Test.py
File metadata and controls
297 lines (234 loc) · 9.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
import re
from pythonwhat.Feedback import Feedback
import numpy as np
import pandas as pd
from pythonwhat.tasks import *
"""
This file contains all tests that can be done on specific objects. All tests are represented
an object. Tests that are alike can inherit from the same superclass. A test is first initialized
and can then be performed by calling the 'test()' function. The result will be stored inside
the result boolean. A test contains a failure message, which can be used by the reporter to
show when the test failed.
"""
class TestFail(Exception):
pass
class Test(object):
"""
The basic Test. It should only contain a failure message, as all tests should result in
a failure message when they fail.
Note:
This test should not be used by itself, subclasses should be used.
Attributes:
feedback (str): A string containing the failure message in case the test fails.
result (bool): True if the test succeed, False if it failed. None if it hasn't been tested yet.
"""
def __init__(self, feedback):
"""
Initialize the standard test.
Args:
feedback: string or Feedback object
"""
if (issubclass(type(feedback), Feedback)):
self.feedback = feedback
elif (issubclass(type(feedback), str)):
self.feedback = Feedback(feedback)
else:
raise TypeError("When creating a test, specify either a string or a Feedback object")
self.result = None
def test(self):
"""
Wrapper around specific tests. Tests only get one chance.
"""
if self.result is None:
try:
self.specific_test()
self.result = np.array(self.result).all()
except:
self.result = False
def specific_test(self):
"""
Perform the actual test. For the standard test, result will be set to False.
"""
self.result = False
def get_feedback(self):
return(self.feedback)
## Testing definition
class DefinedProcessTest(Test):
def __init__(self, name, process, feedback):
super().__init__(feedback)
self.name = name
self.process = process
def specific_test(self):
self.result = isDefinedInProcess(self.name, self.process)
class DefinedCollTest(Test):
"""
Check if an object with a certain name is defined in a collection.
Attributes:
feedback (str): A string containing the failure message in case the test fails.
name (str): Contains the name of the object that is searched for.
collection (list/dict/set): Contains any object on which the 'in' operator can be performed.
result (bool): True if the test succeed, False if it failed. None if it hasn't been tested yet.
"""
def __init__(self, name, collection, feedback):
super().__init__(feedback)
self.name = name
self.collection = collection
def specific_test(self):
self.result = self.name in self.collection
class DefinedCollProcessTest(Test):
def __init__(self, name, key, process, feedback):
super().__init__(feedback)
self.name = name
self.key = key
self.process = process
def specific_test(self):
self.result = isDefinedCollInProcess(self.name, self.key, self.process)
## Testing class
class InstanceTest(Test):
def __init__(self, obj, cls, feedback):
super().__init__(feedback)
self.obj = obj
self.cls = cls
def specific_test(self):
self.result = isinstance(self.obj, self.cls)
class InstanceProcessTest(Test):
def __init__(self, name, klass, process, feedback):
super().__init__(feedback)
self.name = name
self.klass = klass
self.process = process
def specific_test(self):
self.result = isInstanceInProcess(self.name, self.klass, self.process)
## Testing equality
class EqualTest(Test):
"""
Check if two objects are equal. Equal means the objects are exactly the same.
This test should only be used with numeric variables (for now).
Attributes:
feedback (str): A string containing the failure message in case the test fails.
obj1 (str): The first object that should be compared with.
obj2 (str): This object is compared to obj1.
result (bool): True if the test succeed, False if it failed. None if it hasn't been tested yet.
"""
def __init__(self, obj1, obj2, feedback):
super().__init__(feedback)
self.obj1 = obj1
self.obj2 = obj2
def specific_test(self):
"""
Perform the actual test. result is set to False if the objects differ, True otherwise.
"""
self.result = is_equal(self.obj1, self.obj2)
class EqualProcessTest(Test):
def __init__(self, name, student_process, sol_obj, feedback):
super().__init__(feedback)
self.name = name
self.student_process = student_process
self.sol_obj = sol_obj
def specific_test(self):
stud_obj = getRepresentation(self.name, self.student_process)
if isinstance(stud_obj, ReprFail):
self.result = False
else:
self.result = is_equal(stud_obj, self.sol_obj)
class EqualValueProcessTest(Test):
def __init__(self, name, key, student_process, sol_value, feedback):
super().__init__(feedback)
self.name = name
self.key = key
self.student_process = student_process
self.sol_value = sol_value
def specific_test(self):
stud_value, stud_str = getValueInProcess(self.name, self.key, self.student_process)
if isinstance(stud_value, ReprFail):
self.result = False
else:
self.result = is_equal(stud_value, self.sol_value)
## Helpers for testing equality
def objs_are(x, y, list_of_classes):
return (
any([isinstance(x, klass) for klass in list_of_classes]) &
any([isinstance(y, klass) for klass in list_of_classes])
)
def is_equal(x, y):
try:
if objs_are(x, y, [np.ndarray, dict, list]):
np.testing.assert_equal(x, y)
return True
elif objs_are(x, y, [map, filter]):
np.testing.assert_equal(list(x), list(y))
return True
elif objs_are(x, y, [pd.DataFrame]):
pd.util.testing.assert_frame_equal(x, y)
return True
elif objs_are(x, y, [pd.Series]):
pd.util.testing.assert_series_equal(x, y)
return True
elif objs_are(x, y, [Exception]):
assert type(x) == type(y) and str(x) == str(y)
return True
else:
return x == y
except Exception:
self.result = False
## Others
class BiggerTest(Test):
"""
Check if one object is greater than another. This test should only be used with numeric variables (for now).
Attributes:
feedback (str): A string containing the failure message in case the test fails.
obj1 (str): The first object, that should be the greatest
obj2 (str): The second object, that should be smaller
result (bool): True if the test succeed, False if it failed. None if it hasn't been tested yet.
"""
def __init__(self, obj1, obj2, feedback):
"""
Initialize with two objects.
Args:
obj1 (str): The first object, obj1 will be set to this.
obj2 (str): The second object, obj2 will be set to this.
feedback (str): The failure message will be set to this.
"""
super().__init__(feedback)
self.obj1 = obj1
self.obj2 = obj2
def specific_test(self):
"""
Perform the actual test. result is set to False if the objects differ, True otherwise.
"""
self.result = (self.obj1 > self.obj2)
class StringContainsTest(Test):
"""
Check if a string is present in a text. Can use literal strings or a regex.
Attributes:
feedback (str): A string containing the failure message in case the test fails.
string (regex/str): String or regular expression which is searched for.
search_string (str): The text in which is searched.
pattern (bool): If set to True, string is matched with a regex. Literal otherwise.
result (bool): True if the test succeed, False if it failed. None if it hasn't been tested yet.
"""
def __init__(self, string, search_string, pattern, feedback):
"""
Initialize with a string to look for, a string to search and whether or not to look for a pattern.
Args:
string (regex/str): The string to look for will be set to this.
search_string (str): The string to search in will be set to this.
pattern (bool): The pattern boolean will be set to this.
feedback (str): The failure message will be set to this.
"""
super().__init__(feedback)
self.string = string
self.search_string = search_string
self.pattern = pattern
def specific_test(self):
"""
Perform the actual test. result will be True if string is found (whether or not with a pattern),
False otherwise.
"""
if self.pattern:
self.result = (
re.search(
self.search_string,
self.string) is not None)
else:
self.result = (self.string.find(self.search_string) is not -1)