current commit
250526.2 · Support ack3-only
import difflib
import glob
import logging
import os
import subprocess
import sys


try:
    import yaml
except ImportError:
    print('Unable to import yaml module')
    sys.exit(1)


logger = logging.getLogger(__name__)


def test_all_yaml_files():
    """
    Walk all the YAML files and run their tests.
    """
    filenames = sorted(glob.glob('t/*.yaml'))
    for filename in filenames:
        skipped_ack3_only = 0
        logger.info(f'YAML: {filename}')
        with open(filename, 'r', encoding='UTF-8') as f:
            cases = yaml.load_all(f, yaml.FullLoader)
            for case in cases:
                case = massage_case(case)
                if case.get('ack3-only', False):
                    skipped_ack3_only += 1
                else:
                    run_case(case)
            if skipped_ack3_only:
                logger.info(f'    Skipped {skipped_ack3_only} ack3-only case(s)')



def massage_case(case: dict):
    """
    Takes the raw case from the YAML and sets defaults.
    """
    if 'exitcode' not in case:
        case['exitcode'] = 0

    # Make an array of args arrays out of it if it's not already.
    if not isinstance(case['args'], list):
        case['args'] = [case['args']]

    if case['stdout'] is None:
        case['stdout'] = ''

    return case


def expected_lines(case):
    """
    Gets the lines expected from the case, adjusting on the settings.
    """
    lines = case['stdout'].splitlines()
    if indent := case.get('indent-stdout', 0):
        lines = [' ' * indent + x for x in lines]

    return lines


def show_diff(exp, got):
    """
    Shows the diffs between two sets of strings
    """
    diff = '\n'.join(
        difflib.unified_diff(
            exp, got, fromfile='expected', tofile='got', lineterm=''
        )
    )
    print(diff)


def run_case(case):
    """
    Runs an individual case from the YAML.
    """
    logger.info('    Case: %s' % case['name'])
    for args in case['args']:
        if os.getenv('DIRK'):
            command = ['./dirk', '--nocolor']
        else:
            command = ['perl', '-Mblib', 'ack', '--noenv']
        command += args.split()
        logger.info('    Command: %s' % ' '.join(command))

        try:
            result = subprocess.run(
                command,
                input=case.get('stdin', None),
                capture_output=True,
                text=True,
                check=(not case['exitcode']),
            )
        except subprocess.CalledProcessError as e:
            print('STDOUT from', command)
            print(repr(e.stdout))
            print('STDERR from', command)
            print(repr(e.stderr))
            raise

        if case['exitcode']:
            assert result.returncode == case['exitcode']

        exp_lines = expected_lines(case)
        got_lines = result.stdout.splitlines()
        if not case.get('ordered', False):
            got_lines = sorted(got_lines)
            exp_lines = sorted(exp_lines)

        # show_diff(exp_lines, got_lines)
        assert got_lines == exp_lines


test_all_yaml_files()