#!/usr/bin/env python3 import importlib.util import json import pathlib from types import ModuleType from typing import Generator import pytest PROJECT_EULER_DIR_PATH = pathlib.Path.cwd().joinpath("project_euler") PROJECT_EULER_ANSWERS_PATH = PROJECT_EULER_DIR_PATH.joinpath( "project_euler_answers.json" ) with open(PROJECT_EULER_ANSWERS_PATH) as file_handle: PROBLEM_ANSWERS = json.load(file_handle) error_msgs = [] def generate_solution_modules( dir_path: pathlib.Path, ) -> Generator[ModuleType, None, None]: # Iterating over every file or directory for file_path in dir_path.iterdir(): if file_path.suffix != ".py" or file_path.name.startswith(("_", "test")): continue # Importing the source file through the given path # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly spec = importlib.util.spec_from_file_location(file_path.name, str(file_path)) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) yield module @pytest.mark.parametrize("problem_number, expected", PROBLEM_ANSWERS) def test_project_euler(subtests, problem_number: int, expected: str): problem_dir = PROJECT_EULER_DIR_PATH.joinpath(f"problem_{problem_number:02}") # Check if the problem directory exist. If not, then skip. if problem_dir.is_dir(): for solution_module in generate_solution_modules(problem_dir): # All the tests in a loop is considered as one test by pytest so, use # subtests to make sure all the subtests are considered as different. with subtests.test( msg=f"Problem {problem_number} tests", solution_module=solution_module ): try: answer = str(solution_module.solution()) assert answer == expected, f"Expected {expected} but got {answer}" except (AssertionError, AttributeError, TypeError) as err: error_msgs.append( f"problem_{problem_number}/{solution_module.__name__}: {err}" ) raise # We still want pytest to know that this test failed else: pytest.skip(f"Solution {problem_number} does not exist yet.") # Run this function at the end of all the tests # https://stackoverflow.com/a/52873379 @pytest.fixture(scope="session", autouse=True) def custom_print_message(request): def print_error_messages(): if error_msgs: print("\n" + "\n".join(error_msgs)) request.addfinalizer(print_error_messages)