Created
January 24, 2022 18:17
-
-
Save john-kelly/fd054c41d91394597f217fa168856625 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json | |
import operator | |
class expect(object): | |
"""Represents a logical expectation. | |
""" | |
# a static class variable for storing all expectations | |
expectations = [] | |
def __init__(self, actual, negated=False): | |
"""Create an expectation. | |
Parameters | |
---------- | |
actual: primitive | |
The actual on which we're making expectations. | |
negated: boolean | |
Indicates if the expectation should be false. | |
""" | |
self.negated = negated | |
self.actual = actual | |
self.override_test_name = '' | |
self.override_solution_output = '' | |
self.override_student_output = '' | |
self.override_message_pass = '' | |
self.override_message_fail = '' | |
self.override_show_diff = '' | |
def __call__(self): | |
"""Evaluate the expectation.""" | |
return self.assertion() | |
def __make_assertion(self, partial, message, expected): | |
"""Make an assertion operation to evaluate later. | |
Parameters | |
---------- | |
partial: function | |
The assertion to evaluated. | |
message: string | |
A message describing the assertion. | |
expected: primitive | |
The operand for the expression. There may be none. | |
""" | |
def assertion(): | |
passed = bool(int(self.negated) ^ int(partial())) | |
pass_message = ( | |
self.override_message_pass | |
if self.override_message_pass | |
else 'Success' | |
) | |
fail_message = ( | |
self.override_message_fail | |
if self.override_message_fail | |
else 'Failure' | |
) | |
test_name = ( | |
self.override_test_name | |
if self.override_test_name | |
else self._generate_description(message, expected) | |
) | |
student_output = ( | |
self.override_student_output | |
if self.override_student_output | |
else self.actual | |
) | |
result = { | |
# "True" and "False" need to be strings, otherwise they | |
# are lowercased when stringified by JS and cause parse errors. | |
'success': "True" if passed else "False", | |
'test': test_name, | |
'solutionOutput': self.override_solution_output, | |
'studentOutput': student_output, | |
'message': pass_message if passed else fail_message, | |
'showDiff': "True" if self.override_show_diff else "False" | |
} | |
return result | |
self.expectations.append(assertion) | |
self.assertion = assertion | |
return assertion | |
def __partial(self, op, expected): | |
"""Create a partial. | |
Parameters | |
---------- | |
op: object | |
Operator used to compare actual and expected. | |
expected: primitive | |
Item compared against actual. Can be None. | |
Returns function. | |
""" | |
def wrapper(): | |
try: | |
# try to strip whitespace if operands are strings | |
return op(self.actual.strip('\n'), expected.strip('\n')) | |
except AttributeError: | |
# otherwise proceed on non-string operands | |
return op(self.actual, expected) | |
return wrapper | |
def _generate_description(self, message, expected): | |
"""Create an understandable description for the test. | |
Parameters | |
---------- | |
message: string | |
A string describing the expectation. | |
expected: primitive | |
A primitive that was used in the comparison. | |
Returns a string. | |
""" | |
description = "Expected {} {}".format(json.dumps(self.actual), message) | |
description += " {}".format(json.dumps(expected)) | |
return description | |
def to_be(self, expected): | |
"""Create expectation that self.actual == expected. | |
Parameters | |
---------- | |
expected: primitive | |
The primitive to compare with self.actual. | |
Returns an expect object. | |
""" | |
self.__make_assertion(self.__partial(operator.is_, expected), | |
'to be', | |
expected) | |
return self | |
def not_to_be(self, expected): | |
"""Create expectation that self.actual != expected. | |
Parameters | |
---------- | |
expected: primitive | |
The primitive to compare with self.actual. | |
Returns an expect object. | |
""" | |
self.negated = True | |
self.__make_assertion(self.__partial(operator.is_, expected), | |
'not to be', | |
expected) | |
return self | |
def to_be_greater_than(self, expected): | |
"""Create expectation that self.actual > expected. | |
Parameters | |
---------- | |
expected: primitive | |
The primitive to compare with self.actual. | |
Returns an expect object. | |
""" | |
self.__make_assertion(self.__partial(operator.gt, expected), | |
'to be greater than', | |
expected) | |
return self | |
def to_be_greater_than_or_equal_to(self, expected): | |
"""Create expectation that self.actual >= expected. | |
Parameters | |
---------- | |
expected: primitive | |
The primitive to compare with self.actual. | |
Returns an expect object. | |
""" | |
self.__make_assertion(self.__partial(operator.ge, expected), | |
'to be greater than or equal to', | |
expected) | |
return self | |
def to_be_less_than(self, expected): | |
"""Create expectation that self.actual < expected. | |
Parameters | |
---------- | |
expected: primitive | |
The primitive to compare with self.actual. | |
Returns an expect object. | |
""" | |
self.__make_assertion(self.__partial(operator.lt, expected), | |
'to be less than', | |
expected) | |
return self | |
def to_be_less_than_or_equal_to(self, expected): | |
"""Create expectation that self.actual <= expected. | |
Parameters | |
---------- | |
expected: primitive | |
The primitive to compare with self.actual. | |
Returns an expect object. | |
""" | |
self.__make_assertion(self.__partial(operator.le, expected), | |
'to be less than or equal to', | |
expected) | |
return self | |
def to_contain(self, expected): | |
"""Create expectation that self.actual contains expected. | |
Parameters | |
---------- | |
expected: primitive | |
The primitive to search for in self.actual. | |
Returns an expect object. | |
""" | |
self.__make_assertion(self.__partial(operator.contains, expected), | |
'to contain', | |
expected) | |
return self | |
def not_to_contain(self, expected): | |
"""Create expectation that self.actual does not contain expected. | |
Parameters | |
---------- | |
expected: primitive | |
The primitive to search for in self.actual. | |
Returns an expect object. | |
""" | |
self.negated = True | |
self.__make_assertion(self.__partial(operator.contains, expected), | |
'not to contain', | |
expected) | |
return self | |
def to_contain_line(self, expected): | |
"""Create expectation that a line in self.actual contains expected. | |
Parameters | |
---------- | |
expected: primitive | |
The primitive to search for in self.actual. | |
Returns an expect object. | |
""" | |
self.__make_assertion(self.__partial(line_contains, expected), | |
'to contain line', | |
expected) | |
return self | |
def not_to_contain_line(self, expected): | |
"""Create expectation that a line self.actual doesn't contain expected. | |
Parameters | |
---------- | |
expected: primitive | |
The primitive to search for in self.actual. | |
Returns an expect object. | |
""" | |
self.negated = True | |
self.__make_assertion(self.__partial(line_contains, expected), | |
'not to contain line', | |
expected) | |
return self | |
def to_equal(self, expected): | |
"""Create expectation that self.actual is equal to expected. | |
Parameters | |
---------- | |
expected: primitive | |
The primitive to compare with self.actual. | |
Returns an expect object. | |
""" | |
self.__make_assertion(self.__partial(operator.eq, expected), | |
'to equal', | |
expected) | |
return self | |
def not_to_equal(self, expected): | |
"""Create expectation that self.actual is not equal to expected. | |
Parameters | |
---------- | |
expected: primitive | |
The primitive to compare with self.actual. | |
Returns an expect object. | |
""" | |
self.negated = True | |
self.__make_assertion(self.__partial(operator.eq, expected), | |
'not to equal', | |
expected) | |
return self | |
def to_be_truthy(self): | |
"""Create expectation that self.actual is Truthy. | |
Returns an expect object. | |
""" | |
self.__make_assertion(self.__partial(lambda actual, _: bool(actual), None), | |
'to be', | |
'truthy') | |
return self | |
def to_be_falsey(self): | |
"""Create expectation that self.actual is Falsey. | |
Returns an expect object. | |
""" | |
self.negated = True | |
self.__make_assertion(self.__partial(lambda actual, _: bool(actual), None), | |
'to be', | |
'falsey') | |
return self | |
def with_options(self, message_pass='', | |
message_fail='', test_name='', show_diff=False, | |
solution_output='', student_output=''): | |
"""Pass options to override test results. | |
Parameters | |
---------- | |
message_pass: string | |
A message to print on passing. | |
message_fail: string | |
A message to print on failing. | |
test_name: string | |
The name of the test. | |
show_diff: boolean | |
Whether or not to show the diff in the results. | |
solution_output: string | |
A solution output to be shown in the reuslts. | |
""" | |
self.override_message_pass = message_pass if message_pass else self.override_message_pass | |
self.override_message_fail = message_fail if message_fail else self.override_message_fail | |
self.override_test_name = test_name if test_name else self.override_test_name | |
self.override_show_diff = show_diff if show_diff else self.override_show_diff | |
# TODO -- change api. | |
self.override_solution_output = solution_output if solution_output else self.override_solution_output | |
self.override_student_output = student_output if student_output else self.override_student_output | |
return self | |
######################################################## | |
## Below are assertions for Turtle Python. | |
######################################################## | |
def to_contain_command(self, expected): | |
"""Create expectation that self.actual contains the command `expected`. | |
Returns an expect object. | |
""" | |
self.override_test_name = 'Expected your commands to contain {}.'.format(expected) | |
self.__make_assertion(self.__partial(contains_command, expected), | |
'to contain command', | |
expected) | |
return self | |
def line_contains(lhs, rhs): | |
"""An operator to check if a line in lhs string contains rhs. | |
Parameters | |
---------- | |
lhs: string | |
The left hand side of the operation. | |
rhs: string | |
The right hand side of the operation. | |
Returns boolean. | |
""" | |
lines = lhs.split('\n') | |
return rhs in lines | |
def _flush_expectations(): | |
"""Flushes expectations. | |
Returns a list. | |
""" | |
flushed_expectations = expect.expectations | |
expect.expectations = [] | |
return flushed_expectations | |
def contains_command(lhs, rhs): | |
"""An operator to check if a list of commands (lhs) contains command (rhs). | |
Parameters | |
---------- | |
lhs: list | |
The left hand side of the operation, a list of commands. | |
Their format is [('command_name', (arg1, arg2)), ...]. | |
rhs: string | |
The right hand side of the operation, a string of the comand that | |
should bein the lhs. 'forward', 'backward', etc. | |
Returns boolean. | |
""" | |
return any([(command[0] == rhs) for command in lhs]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment