Sat, 25 May 2019 18:37:55 +0200
added some header tests
.hgignore | file | annotate | diff | comparison | revisions | |
header.py | file | annotate | diff | comparison | revisions | |
tests/misc.py | file | annotate | diff | comparison | revisions |
--- a/.hgignore Sat May 25 09:41:33 2019 +0200 +++ b/.hgignore Sat May 25 18:37:55 2019 +0200 @@ -2,3 +2,5 @@ *.dat __pycache__ ldcheck.cfg +*.orig +*.rej
--- a/header.py Sat May 25 09:41:33 2019 +0200 +++ b/header.py Sat May 25 18:37:55 2019 +0200 @@ -1,5 +1,6 @@ import re import linetypes +import datetime class Header: def __init__(self): @@ -10,12 +11,16 @@ self.filetype = None self.qualifiers = None self.license = None - self.help = [] + self.help = '' self.bfc = None self.category = None - self.keywords = [] + self.keywords = '' self.cmdline = None self.history = [] + self.first_occurrence = dict() + @property + def valid(self): + return True class BadHeader: def __init__(self, index, reason): @@ -27,6 +32,9 @@ index = self.index, reason = self.reason, ) + @property + def valid(self): + return False geometrical_types = [ linetypes.LineSegment, @@ -57,6 +65,16 @@ def __str__(self): return reason +class HistoryEntry: + def __init__(self, date, user, text): + self.date, self.user, self.text = date, user, text + def __repr__(self): + return str.format( + 'HistoryEntry({date!r}, {user!r}, {text!r})', + date = self.date, + user = self.user, + text = self.text) + class HeaderParser: def __init__(self): self.model_body = None @@ -64,6 +82,7 @@ self.problems = [] def parse(self, model_body): result = Header() + self.result = result self.order = [] self.cursor = -1 self.model_body = model_body @@ -72,11 +91,11 @@ self.skip_to_next() result.name = self.parse_pattern('^Name: (.+)$', 'name')[0] self.skip_to_next() - result.author = self.parse_pattern('^Author: (.+)$', 'author')[0] + result.author, result.username = self.parse_pattern(r'^Author: ([^\[]+)\s*(?:\[(.+)\])?$', 'author') for header_entry in self.get_more_header_stuff(): if self.try_to_match( '^!LDRAW_ORG ' + - '(' \ + r'(' \ '(?:Unofficial_)?' \ 'Part|' \ 'Subpart|' \ @@ -85,15 +104,48 @@ '48_Primitive|' \ 'Shortcut' \ ')\s?' \ - '(ORIGINAL|UPDATE \d\d\d\d-\d\d)?$', + '(.*)$', 'part type'): - result.filetype, result.qualifiers = self.groups + result.filetype = self.groups[0] + result.qualifiers = re.findall(r'(?:Physical_Colour|Alias|ORIGINAL|UPDATE \d\d\d\d-\d\d)', self.groups[1]) elif self.try_to_match( '^!LICENSE (.+)$', 'license'): - result.license = self.groups() + result.license = self.groups[0] + elif self.try_to_match( + 'BFC (CERTIFY CW|CERTIFY CCW|NOCERTIFY)', + 'bfc'): + result.bfc = self.groups[0] + elif self.try_to_match( + r'!HISTORY (\d{4}-\d{2}-\d{2}) ([\[{][^\]}]+[\]}]) (.+)$', + 'history'): + result.history.append(HistoryEntry( + date = datetime.datetime.strptime(self.groups[0], '%Y-%m-%d').date(), + user = self.groups[1], + text = self.groups[2], + )) + elif self.try_to_match( + r'!HELP (.+)', + 'help'): + if result.help: + result.help += '\n' + result.help += self.groups[0] + elif self.try_to_match( + r'!CATEGORY (.+)', + 'category'): + result.category = self.groups[0] + elif self.try_to_match( + r'!KEYWORDS (.+)', + 'keywords'): + if result.keywords: + result.keywords += '\n' + result.keywords += self.groups[0] + elif self.try_to_match( + r'!CMDLINE (.+)', + 'cmdline'): + result.cmdline = self.groups[0] else: - self.parse_error("couldn't handle header metacommand: " + repr(header_entry.text)) + self.parse_error("couldn't understand header syntax: " + repr(header_entry.text)) return { 'header': result, 'end-index': self.cursor + 1, @@ -123,6 +175,7 @@ def try_to_match(self, pattern, patterntype): try: self.groups = self.parse_pattern(pattern, patterntype) + return True except: return False def current(self): @@ -133,6 +186,8 @@ match = re.search(pattern, self.current()) if match: self.order.append(description) + if description not in self.result.first_occurrence: + self.result.first_occurrence[description] = self.cursor return match.groups() else: self.parse_error(str.format("couldn't parse {}", description))
--- a/tests/misc.py Sat May 25 09:41:33 2019 +0200 +++ b/tests/misc.py Sat May 25 18:37:55 2019 +0200 @@ -25,11 +25,33 @@ reason = model.header.reason, ) +def nocertify_test(model): + import header + if model.header.valid and model.header.bfc == 'NOCERTIFY': + yield error( + model.body[model.header.first_occurrence['bfc']], + 'bfc-nocertify') + +def physical_colours_test(model): + if model.header.valid and 'Physical_Colour' in model.header.qualifiers: + yield error( + model.body[model.header.first_occurrence['part type']], + 'physical-colour') + +def unofficiality_test(model): + if model.header.valid and not model.header.filetype.startswith('Unofficial_'): + yield error( + model.body[model.header.first_occurrence['part type']], + 'unofficial-type') + manifest = { 'tests': { 'colour-validity': colours_test, 'syntax-errors': syntax_errors, 'header-validity': bad_header, + 'bfc-nocertify': nocertify_test, + 'physical-colour': physical_colours_test, + 'unofficial-type': unofficiality_test, }, 'messages': { 'bad-colour': lambda colour_index: str.format( @@ -44,5 +66,8 @@ 'bad header: {}', reason, ), + 'bfc-nocertify': 'all new parts must be BFC certified', + 'physical-colour': 'no new physical colour parts are accepted', + 'unofficial-type': 'new parts must be unofficial', }, }