def cumulative():
    scorer = CumulativeScoreKeeper()

    with let(__scorer, scorer):
        yield

    value(__scorer).suite_finished(scorer.score)
def when(condition):
    '''
    Adds an extra condition
    '''
    old_condition = value(__condition)

    with let(__condition, lambda: old_condition() and condition()):
        yield
def weight(maximum):
    scorer = CumulativeScoreKeeper()

    with let(__scorer, scorer):
        yield

    score = scorer.score

    value(__scorer).suite_finished(score.rescale(maximum))
def __add_callback(event, callback):
    '''
    Helper function. Adds a callback to and event,
    the event being __passed, __failed or __skipped.
    '''
    old_callback = value(event)
    chained = __chain(old_callback, callback)

    with let(event, chained):
        yield
def function_reftest(identifier):
    if not identifier in dir(value(reference_module)):
        raise Exception(f'Bug in tests: reference module does not contain member called "{identifier}"')
    
    with when(defined(identifier, value(tested_module))), let(tested_function_name, identifier):
        reference_function = getattr(value(reference_module), identifier)
        tested_function = getattr(value(tested_module), identifier)

        with check_function_against_reference_implementation(tested=tested_function, reference=reference_function) as check:
            yield check
def all_or_nothing():
    scorer = CumulativeScoreKeeper()

    with let(__scorer, scorer):
        yield

    score = scorer.score

    if not score.is_perfect:
        score = score.zero

    value(__scorer).suite_finished(score)
    def check(*args, **kwargs):
        positional_parameter_names = __get_positional_parameter_names(reference)
        function_name = value(tested_function_name)
        positional_argument_strings = [ f'{name} = {str(arg)}' for name, arg in zip(positional_parameter_names, args) ]
        keyword_argument_strings = [ f'{k}={v}' for k, vi in kwargs.items() ]
        arguments_string = ', '.join(positional_argument_strings + keyword_argument_strings)
        call_string = f'{function_name}({arguments_string})'

        def failure_message_generator(*, failure, **kwargs):
            failure_message = value(__failure_message)
            
            if failure_message:
                return format.vbox(f'Verifying {call_string}', \
                                   format.indent(2, failure_message), \
                                   format.indent(2, str(failure)))
            else:
                raise Exception('Bug in reference tests: no failure message set')

            
        def test_function():
            def check_return_values(expected, actual):
                __failure_message.value = f'Comparing return values'
                assert expected == actual, f'Expected {expected}, got {actual}'

            def check_positional_argument(index, name, expected, actual):
                __failure_message.value = f'Comparing {name} (positional argument #{index + 1})'
                assert expected == actual, f'Expected {expected}, got {actual}'
                
            def check_positional_arguments(names, expecteds, actuals):
                for (index, name, expected, actual) in zip(range(len(names)), names, expecteds, actuals):
                    check_positional_argument(index, name, expected, actual)
                
            actual_args = deepcopy(args)
            actual_kwargs = deepcopy(kwargs)
            expected_args = deepcopy(args)
            expected_kwargs = deepcopy(kwargs)

            __failure_message.value = f'Exception occurred during function call'
            actual_result = tested(*actual_args, **actual_kwargs)
            expected_result = reference(*expected_args, **expected_kwargs)

            check_return_values(expected=expected_result, actual=actual_result)
            check_positional_arguments(positional_parameter_names, expected_args, actual_args)
                

        with reporting.default_failure_message_generator(failure_message_generator), \
             let(__failure_message, None):
            test(test_function)
def setup():
    failure_index = 1

    def failure_callback(*, failure):
        nonlocal failure_index

        if failure_index != 1:
            print('-' * 40)

        generator = value(__failure_message_generator)
        message = generator(failure=failure)
        message_lines = format.hbox(f'[{failure_index}] ', message).format()
        print("\n".join(message_lines))

        failure_index += 1

    with let(__failure_message_generator,
             __dummy_failure_message_generator), on_fail(failure_callback):
        yield
def __run_test(test_function):
    def signal_passed():
        value(__passed)()

    def signal_failed(exception):
        value(__failed)(failure=exception)

    def signal_skipped():
        value(__skipped)()

    if __should_test_run():
        with let(__test_function, test_function):
            try:
                test_function()
                signal_passed()
            except NotImplementedError:
                signal_skipped()
            except BaseException as e:
                signal_failed(e)
    else:
        signal_skipped()
def score(func):
    '''
    Sets up scoring mechanism. All tests to be scored are to be placed inside a function,
    given as parameter to this function.

    Returns the final score of the tests in func.
    '''
    def passed():
        value(__scorer).test_passed()

    def failed(*args, **kwargs):
        value(__scorer).test_failed()

    def skipped():
        value(__scorer).test_skipped()

    scorer = CumulativeScoreKeeper()

    with let(__scorer,
             scorer), on_pass(passed), on_fail(failed), on_skip(skipped):
        func()

    return scorer.score
def failure_message_generator(message_generator):
    with let(__failure_message_generator, message_generator):
        yield