-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
128 changed files
with
226,938 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--- | ||
tests_timeout_ms: 10000 | ||
sandbox_image: eu.gcr.io/moocfi-public/tmc-sandbox-python |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Write your solution here | ||
from datetime import datetime | ||
|
||
from datetime import datetime | ||
|
||
def calculate_age(year: int, month: int, day: int): | ||
birthday = datetime(year, month, day) | ||
millenium = datetime(1999, 12, 31) | ||
difference = millenium - birthday | ||
return difference.days | ||
|
||
day = int(input('Day: ')) | ||
month = int(input('Month: ')) | ||
year = int(input('Year: ')) | ||
|
||
age_by_millenium = calculate_age(year, month, day) | ||
if age_by_millenium > 0: | ||
print(f'You were {age_by_millenium} days old on the eve of the new millennium.') | ||
else: | ||
print(f"You weren't born yet on the eve of the new millennium.") |
Empty file.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import unittest | ||
from unittest.mock import patch | ||
|
||
from tmc import points | ||
from tmc.utils import load, load_module, reload_module, get_stdout | ||
from functools import reduce | ||
import os | ||
import os.path | ||
import textwrap | ||
from random import choice, randint | ||
|
||
exercise = 'src.how_old' | ||
|
||
def x(t): | ||
return "\n"+"\n".join(t) | ||
|
||
@points('7.how_old') | ||
class HowOldTest(unittest.TestCase): | ||
@classmethod | ||
def setUpClass(cls): | ||
with patch('builtins.input', side_effect=["30","12","1999"]): | ||
cls.module = load_module(exercise, 'en') | ||
|
||
|
||
def test1_uses_import_expression(self): | ||
with open("src/how_old.py") as f: | ||
cont = f.read() | ||
self.assertTrue("import" in cont and "datetime" in cont, | ||
f"Your program does not import datetime-library with the import expression.") | ||
|
||
def test2_test_with_older_ones(self): | ||
test_cases = {("1","1","1900"): "36523", ("10","9","1977"): "8147", ("30","12","1999"): 1, ("6","5","1995"): "1700"} | ||
for test_case in test_cases: | ||
with patch('builtins.input', side_effect=list(test_case)): | ||
try: | ||
reload_module(self.module) | ||
except: | ||
self.fail(f"Try to execute your program with the following inputs {x(test_case)}") | ||
|
||
output = "\n".join([x.strip() for x in get_stdout().split("\n") if len(x.strip()) > 0]) | ||
lines = len(output.split("\n")) | ||
correct = f"You were {test_cases[test_case]} days old on the eve of the new millennium." | ||
msg = 'Note, that in this program no code must not be placed inside the if __name__ == "main" -block.' | ||
|
||
self.assertTrue(lines > 0, f"Your program does not print out anything, {msg}") | ||
|
||
self.assertTrue(lines == 1, | ||
f"Your program is expected to print out 1 row, now it prints out {lines} rows: \n{output}\nwhen the input is {x(test_case)}") | ||
|
||
self.assertTrue(correct in output, | ||
f"Row {correct} is expected to be found out from your program, when the input is {x(test_case)}\nnow print out is \n{output}") | ||
|
||
def test3_test_with_younger_ones(self): | ||
test_cases = [("1","1","2100"), ("10","9","2019"), ("1","1","2000")] | ||
for test_case in test_cases: | ||
with patch('builtins.input', side_effect=list(test_case)): | ||
try: | ||
reload_module(self.module) | ||
except: | ||
self.fail(f"Try to execute your program with the following inputs {x(test_case)}") | ||
|
||
output = "\n".join([x.strip() for x in get_stdout().split("\n") if len(x.strip()) > 0]) | ||
lines = len(output.split("\n")) | ||
correct = "You weren't born yet on the eve of the new millennium." | ||
|
||
self.assertTrue(lines == 1, | ||
f"Your program is expected to print out 1 row, now it prints out {lines} rows: \n{output}\nwhen the input is {x(test_case)}") | ||
|
||
self.assertTrue(correct in output, | ||
f"Row {correct} is expected to be found out from your program, when the input is {x(test_case)}\nnow print out is \n{output}") | ||
|
||
if __name__ == '__main__': | ||
unittest.main() |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .points import points | ||
from .runner import TMCTestRunner |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from unittest import TestProgram | ||
from .runner import TMCTestRunner | ||
import sys | ||
|
||
|
||
if sys.argv.__len__() > 1 and sys.argv[1] == 'available_points': | ||
TMCTestRunner().available_points() | ||
sys.exit() | ||
|
||
main = TestProgram | ||
main(testRunner=TMCTestRunner, module=None, failfast=False, buffer=True) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from inspect import isclass, isfunction | ||
from collections import defaultdict | ||
|
||
point_register = {'suite': defaultdict(list), 'test': defaultdict(list)} | ||
|
||
|
||
def qualifier(test): | ||
return "%s.%s" % (test.__module__, test.__qualname__) | ||
|
||
|
||
def save_points(o, points, dst): | ||
q = qualifier(o) | ||
dst[q] += filter(lambda point: point not in dst[q], points) | ||
|
||
|
||
def points(*points): | ||
|
||
def points_wrapper(o): | ||
if isclass(o): | ||
save_points(o, points, point_register['suite']) | ||
elif isfunction(o): | ||
save_points(o, points, point_register['test']) | ||
else: | ||
raise Exception("Expected decorator object '%s' type to be Class or Function but was %s." % (o, type(o))) | ||
return o | ||
|
||
if not points: | ||
raise Exception("You need to define at least one point in the points decorator declaration") | ||
for point in points: | ||
if type(point) is not str: | ||
msg = "Points decorator argument '%s' needs to be a string, but was %s." % (point, type(point).__name__) | ||
raise Exception(msg) | ||
return points_wrapper | ||
|
||
|
||
def _parse_points(test): | ||
name = _name_test(test) | ||
testPoints = point_register['test'] | ||
points = testPoints[name] | ||
key = name[:name.rfind('.')] | ||
suitePoints = point_register['suite'][key] | ||
points += suitePoints | ||
return points | ||
|
||
|
||
def _name_test(test): | ||
module = test.__module__ | ||
classname = test.__class__.__name__ | ||
testName = test._testMethodName | ||
return module + '.' + classname + '.' + testName |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from unittest.runner import TextTestResult | ||
from .points import _parse_points, _name_test | ||
import atexit | ||
import json | ||
import traceback | ||
|
||
results = [] | ||
|
||
|
||
class TMCResult(TextTestResult): | ||
|
||
def __init__(self, stream, descriptions, verbosity): | ||
super(TMCResult, self).__init__(stream, descriptions, verbosity) | ||
|
||
def startTest(self, test): | ||
super(TMCResult, self).startTest(test) | ||
|
||
def addSuccess(self, test): | ||
super(TMCResult, self).addSuccess(test) | ||
self.addResult(test, 'passed') | ||
|
||
def addFailure(self, test, err): | ||
super(TMCResult, self).addFailure(test, err) | ||
self.addResult(test, 'failed', err) | ||
|
||
def addError(self, test, err): | ||
super(TMCResult, self).addError(test, err) | ||
self.addResult(test, 'errored', err) | ||
|
||
def addResult(self, test, status, err=None): | ||
points = _parse_points(test) | ||
message = "" | ||
backtrace = [] | ||
if err is not None: | ||
message = str(err[1]) | ||
backtrace = traceback.format_tb(err[2]) | ||
|
||
details = { | ||
'name': _name_test(test), | ||
'status': status, | ||
'message': message, | ||
'passed': status == 'passed', | ||
'points': points, | ||
'backtrace': backtrace | ||
} | ||
results.append(details) | ||
|
||
# TODO: Do not do this if not using TMCTestRunner | ||
@atexit.register | ||
def write_output(): | ||
with open('.tmc_test_results.json', 'w', encoding='utf8') as f: | ||
json.dump(results, f, ensure_ascii=False) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
from unittest import TextTestRunner, TestLoader | ||
from .result import TMCResult | ||
from .points import _parse_points, _name_test | ||
from itertools import chain | ||
import json | ||
|
||
|
||
class TMCTestRunner(TextTestRunner): | ||
"""A test runner for TMC exercises. | ||
""" | ||
|
||
resultclass = TMCResult | ||
|
||
def __init__(self, *args, **kwargs): | ||
super(TMCTestRunner, self).__init__(*args, **kwargs) | ||
|
||
def run(self, test): | ||
print('Running tests with some TMC magic...') | ||
return super(TMCTestRunner, self).run(test) | ||
|
||
def available_points(self): | ||
testLoader = TestLoader() | ||
tests = testLoader.discover('.', 'test*.py', None) | ||
try: | ||
tests = list(chain(*chain(*tests._tests))) | ||
except Exception as error: | ||
print("Received following Exception:", error) | ||
tests.debug() | ||
|
||
points = map(_parse_points, tests) | ||
names = map(_name_test, tests) | ||
|
||
result = dict(zip(names, points)) | ||
|
||
with open('.available_points.json', 'w') as f: | ||
json.dump(result, f, ensure_ascii=False) |
Oops, something went wrong.