added some header tests

Sat, 25 May 2019 18:37:55 +0200

author
Teemu Piippo <teemu@hecknology.net>
date
Sat, 25 May 2019 18:37:55 +0200
changeset 48
38b0919c1934
parent 47
4da025d0b283
child 49
a1f5c12fa45c

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',
     },
 }

mercurial