diff options
author | jwansek <eddie.atten.ea29@gmail.com> | 2022-01-19 17:05:53 +0000 |
---|---|---|
committer | jwansek <eddie.atten.ea29@gmail.com> | 2022-01-19 17:05:53 +0000 |
commit | 0404c5d319d0cf85941b20e53a7dab1e9bf71bef (patch) | |
tree | 7ca3384756c34826f41c7624064b952a98023f83 | |
parent | eb5b1d2889aa5e408309f5d89989768ff19bc7f4 (diff) | |
download | Smarker-0404c5d319d0cf85941b20e53a7dab1e9bf71bef.tar.gz Smarker-0404c5d319d0cf85941b20e53a7dab1e9bf71bef.zip |
added testing and generating testing reports
-rw-r--r-- | ExampleAssessments/CMP-4009B.yml | 22 | ||||
-rw-r--r-- | ExampleAssessments/example.yml | 18 | ||||
-rw-r--r-- | ExampleSubmission/example.py | 14 | ||||
-rw-r--r-- | ExampleSubmission/test_dont_test_me.py | 7 | ||||
-rw-r--r-- | mark.py | 5 | ||||
-rw-r--r-- | reflect.py | 69 | ||||
-rw-r--r-- | requirements.txt | 1 |
7 files changed, 116 insertions, 20 deletions
diff --git a/ExampleAssessments/CMP-4009B.yml b/ExampleAssessments/CMP-4009B.yml index 571c97f..fbadc90 100644 --- a/ExampleAssessments/CMP-4009B.yml +++ b/ExampleAssessments/CMP-4009B.yml @@ -1,3 +1,4 @@ +assessment_name: CMP-4009B files: - pjtool.py: classes: @@ -11,18 +12,15 @@ files: - numMonths(2) tests: - | - d1 = Date(2001, 8, 12) - d2 = Date(2001, 8, 12) - return d1 == d2 + d1 = pjtool.Date(2001, 8, 12) + d2 = pjtool.Date(2001, 8, 12) + assert d1 == d2 + - | + d1 = pjtool.Date(2001, 8, 12) + d2 = pjtool.Date(1999, 4, 5) + assert d1 != d2 - tester.py: functions: - dateTester(2) - printIfExecuted: False - - turbine.py: - classes: - - Turbine: - attributes: - - rating:int - - aNonExistantModule.py: - functions: - - aNonExistantFunction(1)
\ No newline at end of file +dependencies: + - matplotlib
\ No newline at end of file diff --git a/ExampleAssessments/example.yml b/ExampleAssessments/example.yml index bab1fb0..17ab9b0 100644 --- a/ExampleAssessments/example.yml +++ b/ExampleAssessments/example.yml @@ -12,6 +12,15 @@ files: - an_undocumented_function(0) - aFunctionThatIsntThere(2) - greet(2) + tests: + - | + dateOne = example.MyDate(day = 12, month = 8, year = 2001) + dateTwo = example.MyDate(day = 12, month = 8, year = 2001) + assert dateOne == dateTwo + - | + dateOne = example.MyDate(day = 12, month = 8, year = 2001) + dateTwo = example.MyDate(day = 5, month = 4, year = 1999) + assert dateOne == dateTwo - aFileThatIsntThere.py: functions: - hello_world(2) @@ -19,4 +28,11 @@ files: classes: - Dog: - Cat: - - Kitten:
\ No newline at end of file + - Kitten: + tests: + - | + nibbles = animals.Kitten() + assert nibbles.speak() == "nyaa~~" + - | + milton = animals.Dog() + assert milton.move() == "*moves*"
\ No newline at end of file diff --git a/ExampleSubmission/example.py b/ExampleSubmission/example.py index a226adb..cc61122 100644 --- a/ExampleSubmission/example.py +++ b/ExampleSubmission/example.py @@ -2,6 +2,7 @@ # 12-01-21 import tkinter as tk +from dataclasses import dataclass class Application(tk.Tk): """An example class, which implements a GUI by inheriting from tkinter.Tk @@ -35,6 +36,19 @@ class Application(tk.Tk): """ return num1 + num2 +@dataclass +class MyDate: + year:int + month:int + day:int + + def __eq__(self, otherDate): + return self.year == otherDate.year and self.month == otherDate.month and self.day == otherDate.day + + def __str__(self): + "%d-%d-%4d" % (self.day, self.month, self.year) + + # hello world! def hello_world(times): """Prints 'hello world!' to stdout. Prints it out `times` times. diff --git a/ExampleSubmission/test_dont_test_me.py b/ExampleSubmission/test_dont_test_me.py new file mode 100644 index 0000000..511c713 --- /dev/null +++ b/ExampleSubmission/test_dont_test_me.py @@ -0,0 +1,7 @@ +"""My default pytest will assume that all files prefixed with +'test' are test files. This file is here to make sure that +pytest only runs on the files it should run on. +""" + +def test_1(): + assert 1 == 2
\ No newline at end of file @@ -25,7 +25,7 @@ def main(**kwargs): with open(kwargs["assessment"], "r") as f: assessment_struct = yaml.safe_load(f) - output = reflect.gen_reflection_report(submission_files, assessment_struct) + output = reflect.gen_reflection_report(submission_files, assessment_struct, kwargs) output_file = kwargs["out"] if kwargs["format"] == "yaml": strout = yaml.dump(output) @@ -34,6 +34,7 @@ def main(**kwargs): if output_file == "stdout": print(strout) + # input("\n\n[tempdir: %s] Press any key to close..." % tempdir) exit() if output_file == "auto": @@ -42,6 +43,8 @@ def main(**kwargs): with open(output_file, "w") as f: f.write(strout) + # input("\n\n[tempdir: %s] Press any key to close..." % tempdir) + if __name__ == "__main__": config = configparser.ConfigParser() config.read("smarker.conf") @@ -1,9 +1,13 @@ +import xml.etree.ElementTree as etree from dataclasses import dataclass from functools import reduce from operator import getitem +import subprocess import importlib +import tempfile import inspect import pkgutil +import shutil import sys import os import re @@ -17,6 +21,7 @@ class Reflect: self.client_code_path = os.path.normpath(self.client_code_path) sys.path.insert(1, self.client_code_path) self.client_modules = [p for p in pkgutil.iter_modules() if os.path.normpath(str(p[0])[12:-2]) == self.client_code_path] + # print("client moduules ", self.client_modules) def import_module(self, module_name): """Imports a module. Before reflection can be conducted, a module @@ -98,6 +103,15 @@ class Reflect: } def get_class_full_name(self, class_): + """Returns the name of a class object as a nice string. e.g. modulename.classname + except if it's a builtin there'll be no module name. + + Args: + class_ (class): A class to get the name of + + Returns: + str: A nicely formatted class name. + """ if class_.__module__ in ['builtins', 'exceptions']: return class_.__name__ return "%s.%s" % (class_.__module__, class_.__name__) @@ -143,14 +157,48 @@ class Reflect: # return inspect.getclasstree(classes) return tree - def get_source_code(self, file_, line_start, line_end): - with open(file=file_, mode="r") as f: - return f.readlines()[line_start:line_end] - -def gen_reflection_report(client_code_path, assessment_struct): + def run_tests(self, tests, run_colourful = False): + test_results = {} + test_results["pytest_report"] = "" + for filename, filestests in tests.items(): + with open(os.path.join(self.client_code_path, "test_" + filename), "a") as f: + for m in self.client_modules: + f.write("import %s\n" % m.name) + f.write("\n") + + for i, test_code in enumerate(filestests, 1): + f.write("def test_%d():\n" % i) + for line in test_code.split("\n"): + f.write(" %s\n" % line.rstrip()) + f.write("\n") + + with tempfile.TemporaryDirectory() as tmp: + junitxmlpath = os.path.join(tmp, "report.xml") + cmd = ["pytest", "-v"] + [os.path.join(self.client_code_path, "test_%s" % f) for f in tests.keys()] + ["--junitxml=%s" % junitxmlpath] + #print(" ".join(cmd)) + proc = subprocess.Popen(cmd, stdout = subprocess.PIPE) + while True: + line = proc.stdout.readline() + if not line: + break + test_results["pytest_report"] += line.decode() + + with open(junitxmlpath, "r") as f: + test_results["junitxml"] = f.read() + root = etree.fromstring(test_results["junitxml"]) + test_results["meta"] = root.findall("./testsuite")[0].attrib + + if run_colourful: + subprocess.run(cmd) + + return test_results + +def gen_reflection_report(client_code_path, assessment_struct, configuration): + print(configuration) reflection = Reflect(client_code_path) present_module_names = [i.name for i in reflection.client_modules] out = assessment_struct + tests_to_run = {} for i, required_file in enumerate(assessment_struct["files"], 0): required_file = list(required_file.keys())[0] @@ -223,8 +271,17 @@ def gen_reflection_report(client_code_path, assessment_struct): out["files"][i][required_file]["functions"][j][required_function]["minimum_arguments"] = present_functions[function_name][-2].count(",") + 1 out["files"][i][required_file]["functions"][j][required_function]["source_code"] = present_functions[function_name][-2] + if "tests" in required_files_features.keys(): + filename = list(assessment_struct["files"][i].keys())[0] + for j, test in enumerate(assessment_struct["files"][i][required_file]["tests"], 0): + try: + tests_to_run[filename].append(test) + except KeyError: + tests_to_run[filename] = [test] + + out["test_results"] = reflection.run_tests(tests_to_run, configuration["out"] == "stdout") out["class_tree"] = reflection.get_class_tree() - return out + # return out if __name__ == "__main__": user_code_path = "D:\\Edencloud\\UniStuff\\3.0 - CMP 3rd Year Project\\Smarker\\../ExampleSubmissions/Submission_A" diff --git a/requirements.txt b/requirements.txt index bee6c14..8b2a354 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ PyYAML==6.0 +pytest |