tests/misc.py

Mon, 24 Jun 2019 00:18:19 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Mon, 24 Jun 2019 00:18:19 +0300
changeset 81
e65d82501a38
parent 78
1970afe52da4
child 86
b25fc6f7eb70
permissions
-rw-r--r--

added better help entries to command line parameters

from testsuite import problem_type, report_problem
import linetypes

@problem_type('bad-colour',
    severity = 'hold',
    message = lambda colour_index, count: str.format(
        'invalid colour {} used {} time(s)',
        colour_index,
        count,
    ),
)
def colours_test(model):
    ''' Checks that all colours used in the part model are valid. '''
    from collections import defaultdict
    bad_colours = defaultdict(lambda: {'count': 0, 'first-occurrence': None})
    for element in model.body:
        if hasattr(element, 'colour') and not element.colour.is_valid:
            bad_colours[element.colour.index]['count'] += 1
            if not bad_colours[element.colour.index]['first-occurrence']:
                bad_colours[element.colour.index]['first-occurrence'] = element
    yield from [
        report_problem(
            'bad-colour',
            bad_object = bad_colour['first-occurrence'],
            colour_index = colour_index,
            count = bad_colour['count'],
        )
        for colour_index, bad_colour in bad_colours.items()
    ]

@problem_type('syntax-error',
    severity = 'hold',
    message = lambda reason: str.format('syntax error: {}', reason),
)
def syntax_errors(model):
    yield from (
        report_problem('syntax-error',
            bad_object = element,
            reason = element.reason
        )
        for element in model.body
        if isinstance(element, linetypes.Error)
    )

@problem_type('bad-header',
    severity = 'hold',
    message = lambda reason: str.format('bad header: {}', reason),
)
def bad_header(model):
    import header
    if isinstance(model.header, header.BadHeader):
        yield report_problem(
            'bad-header',
            bad_object = model.body[model.header.index],
            reason = model.header.reason,
        )

@problem_type('bfc-nocertify',
    severity = 'hold',
    message = 'all new parts must be BFC certified',
)
def nocertify_test(model):
    import header
    if model.header.valid and model.header.bfc == 'NOCERTIFY':
        yield report_problem(
            'bfc-nocertify',
            bad_object = model.find_first_header_object('bfc'),
        )

@problem_type('physical-colour-part',
    severity = 'hold',
    message = 'no new physical colour parts are accepted',
)
def physical_colours_test(model):
    if model.header.valid and 'Physical_Colour' in model.header.qualifiers:
        yield report_problem(
            'physical-colour-part',
            bad_object = model.find_first_header_object('part type'),
        )

@problem_type('unofficial-part',
    severity = 'hold',
    message = 'new parts must be unofficial',
)
def unofficiality_test(model):
    if model.header.valid and not model.header.filetype.startswith('Unofficial_'):
        yield report_problem(
            'unofficial-part',
            bad_object = model.find_first_header_object('part type')
        )

@problem_type('primitive-ccw',
    severity = 'hold',
    message = 'primitives must have CCW winding',
)
@problem_type('no-bfc-line',
    severity = 'hold',
    message = 'BFC declaration is missing',
)
def header_bfc_test(model):
    if model.header.valid and not model.header.bfc:
        yield report_problem(
            'no-bfc-line',
            bad_object = model.body[0],
        )
    elif model.header.valid \
    and model.header.filetype.endswith('Primitive') \
    and model.header.bfc != 'CERTIFY CCW':
        yield report_problem(
            'primitive-bfc-ccw',
            bad_object = model.find_first_header_object('bfc'),
        )

@problem_type('keywords-for-nonparts',
    severity = 'warning',
    message = lambda type: str.format(
        'keywords are not allowed for {type} files',
        type = type,
    ),
)
def keywords_tests(model):
    if model.header.valid:
        if model.header.keywords \
        and model.header.effective_filetype != 'Part':
            yield report_problem(
                'keywords-for-nonparts',
                bad_object = model.find_first_header_object('keywords'),
                type = model.header.effective_filetype,
            )

@problem_type('bad-colour-24-nonline',
    severity = 'hold',
    message = 'colour 24 used on non-lines',
)
@problem_type('bad-colour-24-line',
    severity = 'hold',
    message = 'line with colour other than 24',
)
def colour_24_test(model):
    for element in model.body:
        if hasattr(element, 'colour'):
            is_line = isinstance(element, linetypes.LineSegment)
            if not is_line and element.colour.index == 24:
                yield report_problem('bad-colour-24-nonline', bad_object = element)
            if is_line and element.colour.index != 24:
                yield report_problem('bad-colour-24-line', bad_object = element)

@problem_type('moved-to-with-extension',
    severity = 'hold',
    message = 'moved-to files must not contain the '
        '".dat"-extension in the description',
)
def moved_to_with_extension_test(model):
    if model.header.valid \
    and model.header.description.startswith('~Moved to') \
    and '.dat' in model.header.description:
        yield report_problem(
            'moved-to-with-extension',
            bad_object = model.body[0],
        )

@problem_type('bfc-invertnext-not-on-subfile',
    severity = 'hold',
    message = '"BFC INVERTNEXT" not followed by a type-1 line',
)
def bfc_invertnext_not_on_subfile_test(model):
    def get_invertnexts(model):
        yield from [
            (index, element)
            for index, element in enumerate(model.body)
            if isinstance(element, linetypes.MetaCommand) \
                and element.text == 'BFC INVERTNEXT'
        ]
    def has_subfile_after_invertnext(index):
        index_subfile = index + 1 # subfile reference should be on the next line
        if index_subfile >= len(model.body):
            return False # past the end...
        else:
            element = model.body[index_subfile]
            return isinstance(element, linetypes.SubfileReference)
    for index, invertnext in get_invertnexts(model):
        if not has_subfile_after_invertnext(index):
            yield report_problem('bfc-invertnext-not-on-subfile',
                bad_object = model.body[index],
            )

@problem_type('unknown-metacommand',
    severity = 'hold',
    message = lambda command_text: str.format(
        'unknown or deprecated metacommand: {command_text}',
        command_text = command_text,
    )
)
def metacommands_test(model):
    allowed_metacommand_patterns = [
        r'^BFC (CLIP|NOCLIP|INVERTNEXT)$',
        r'^\!TEXMAP (START|NEXT) .+',
        r'^\!: .+',
        r'^\!TEXMAP (FALLBACK|END)$',
    ]
    import re
    for element in model.body[model.header_size:]:
        if isinstance(element, linetypes.MetaCommand):
            if element.text and not any(
                re.match(pattern, element.text)
                for pattern in allowed_metacommand_patterns
            ):
                yield report_problem('unknown-metacommand',
                    bad_object = element,
                    command_text = element.text,
                )

manifest = {
    'tests': [
        colours_test,
        syntax_errors,
        bad_header,
        nocertify_test,
        physical_colours_test,
        unofficiality_test,
        header_bfc_test,
        keywords_tests,
        colour_24_test,
        moved_to_with_extension_test,
        bfc_invertnext_not_on_subfile_test,
        metacommands_test,
    ],
}

mercurial