Add base support for markup tests.

This commit is contained in:
Sergio Martínez Portela 2020-06-27 19:20:34 +02:00
parent d23ee1adba
commit 0dab7e4703
6 changed files with 209 additions and 22 deletions

View File

@ -1 +1,2 @@
from .org_dom import * from .org_dom import *
from .utils import *

View File

@ -55,12 +55,14 @@ INACTIVE_TIME_STAMP_RE = re.compile(r'\[{}\]'.format(BASE_TIME_STAMP_RE))
# r'(?P<end_year>\d{4})-(?P<end_month>\d{2})-(?P<end_day>\d{2}) (?P<end_dow>[^ ]+)((?P<end_hour>\d{1,2}):(?P<end_minute>\d{1,2}))?') # r'(?P<end_year>\d{4})-(?P<end_month>\d{2})-(?P<end_day>\d{2}) (?P<end_dow>[^ ]+)((?P<end_hour>\d{1,2}):(?P<end_minute>\d{1,2}))?')
Headline = collections.namedtuple('Headline', ('start_line', 'depth', Headline = collections.namedtuple('Headline', ('start_line', 'depth',
'keyword_start', 'keyword', 'orig',
'properties', 'keywords',
'priority_start', 'priority', 'priority_start', 'priority',
'title_start', 'title', 'title_start', 'title',
'tags_start', 'tags', 'tags_start', 'tags',
'content', 'contents',
'children', 'children',
'structural',
)) ))
RawLine = collections.namedtuple('RawLine', ('linenum', 'line')) RawLine = collections.namedtuple('RawLine', ('linenum', 'line'))
@ -107,9 +109,43 @@ def timestamp_to_string(ts):
else: else:
return '[{}]'.format(base) return '[{}]'.format(base)
def parse_headline(hl) -> Headline:
# 'linenum': linenum,
# 'orig': match,
# 'title': match.group('line'),
# 'contents': [],
# 'children': [],
# 'keywords': [],
# 'properties': [],
# 'structural': [],
# HEADLINE_RE = re.compile(r'^(?P<stars>\*+) (?P<spacing>\s*)(?P<line>.*)$')
stars = hl['orig'].group('stars')
depth = len(stars)
# TODO: Parse line for priority, cookies and tags
line = hl['orig'].group('line')
title = line.strip()
return Headline(start_line=hl['linenum'],
depth=depth,
orig=hl['orig'],
title=title,
contents=hl['contents'],
children=[parse_headline(child) for child in hl['children']],
keywords=hl['keywords'],
properties=hl['properties'],
structural=hl['structural'],
title_start=None,
priority=None,
priority_start=None,
tags_start=None,
tags=None,
)
class OrgDom: class OrgDom:
def __init__(self, headlines, keywords, contents): def __init__(self, headlines, keywords, contents):
self.headlines: List[Headline] = headlines self.headlines: List[Headline] = list(map(parse_headline, headlines))
self.keywords: List[Property] = keywords self.keywords: List[Property] = keywords
self.contents: List[RawLine] = contents self.contents: List[RawLine] = contents
@ -162,23 +198,23 @@ class OrgDom:
return (structural[0], structural[1]) return (structural[0], structural[1])
def dump_headline(self, headline): def dump_headline(self, headline):
yield headline['orig'].group('stars') + ' ' + headline['orig'].group('spacing') + headline['orig'].group('line') yield '*' * headline.depth + ' ' + headline.orig.group('spacing') + headline.title
lines = [] lines = []
KW_T = 0 KW_T = 0
CONTENT_T = 1 CONTENT_T = 1
PROPERTIES_T = 2 PROPERTIES_T = 2
STRUCTURAL_T = 3 STRUCTURAL_T = 3
for keyword in headline['keywords']: for keyword in headline.keywords:
lines.append((KW_T, self.dump_kw(keyword))) lines.append((KW_T, self.dump_kw(keyword)))
for content in headline['contents']: for content in headline.contents:
lines.append((CONTENT_T, self.dump_contents(content))) lines.append((CONTENT_T, self.dump_contents(content)))
for prop in headline['properties']: for prop in headline.properties:
lines.append((PROPERTIES_T, self.dump_property(prop))) lines.append((PROPERTIES_T, self.dump_property(prop)))
for struct in headline['structural']: for struct in headline.structural:
lines.append((STRUCTURAL_T, self.dump_structural(struct))) lines.append((STRUCTURAL_T, self.dump_structural(struct)))
lines = sorted(lines, key=lambda x: x[1][0]) lines = sorted(lines, key=lambda x: x[1][0])
@ -204,7 +240,7 @@ class OrgDom:
yield from structured_lines yield from structured_lines
for child in headline['children']: for child in headline.children:
yield from self.dump_headline(child) yield from self.dump_headline(child)
def dump(self): def dump(self):
@ -235,7 +271,7 @@ class OrgDomReader:
def add_headline(self, linenum: int, match: re.Match) -> int: def add_headline(self, linenum: int, match: re.Match) -> int:
# Position reader on the proper headline # Position reader on the proper headline
stars = match.group('stars') stars = match.group('stars')
depth = len(stars) - 1 depth = len(stars)
headline = { headline = {
'linenum': linenum, 'linenum': linenum,
@ -248,13 +284,13 @@ class OrgDomReader:
'structural': [], 'structural': [],
} }
while (depth - 1) > len(self.headline_hierarchy): while (depth - 2) > len(self.headline_hierarchy):
# Introduce structural headlines # Introduce structural headlines
self.headline_hierarchy.append(None) self.headline_hierarchy.append(None)
while depth < len(self.headline_hierarchy): while depth < len(self.headline_hierarchy):
self.headline_hierarchy.pop() self.headline_hierarchy.pop()
if depth == 0: if depth == 1:
self.headlines.append(headline) self.headlines.append(headline)
else: else:
self.headline_hierarchy[-1]['children'].append(headline) self.headline_hierarchy[-1]['children'].append(headline)

22
org_dom/utils.py Normal file
View File

@ -0,0 +1,22 @@
from .org_dom import Headline, RawLine
def get_hl_raw_contents(doc: Headline) -> str:
lines = []
for content in doc.contents:
lines.append(get_raw_contents(content))
return '\n'.join(lines)
def get_rawline_contents(doc: RawLine) -> str:
return doc.line
def get_raw_contents(doc) -> str:
if isinstance(doc, Headline):
return get_hl_raw_contents(doc)
if isinstance(doc, RawLine):
return get_rawline_contents(doc)
raise NotImplementedError('Unhandled type: ' + str(doc))

21
tests/02-markup.org Normal file
View File

@ -0,0 +1,21 @@
#+TITLE: 02-Markup
#+DESCRIPTION: Simple org file to test markup
#+TODO: TODO(t) PAUSED(p) | DONE(d)
* First level
:PROPERTIES:
:ID: 02-markup-first-level-id
:CREATED: [2020-01-01 Wed 01:01]
:END:
This is a *bold phrase*.
This is a =verbatim phrase=.
This is a /italic phrase/.
This is a +strike-through phrase+.
This is a _underlined phrase_.
This is a ~code phrase~.

View File

@ -4,7 +4,8 @@ import unittest
from datetime import datetime as DT from datetime import datetime as DT
from org_dom import dumps, load, loads from org_dom import dumps, load, loads
from utils.dom_assertions import HL, Dom from utils.dom_assertions import (BOLD, CODE, HL, ITALIC, SPAN, STRIKE,
UNDERLINED, VERBATIM, Dom)
DIR = os.path.dirname(os.path.abspath(__file__)) DIR = os.path.dirname(os.path.abspath(__file__))
@ -23,15 +24,15 @@ class TestSerde(unittest.TestCase):
('ID', '01-simple-first-level-id'), ('ID', '01-simple-first-level-id'),
('CREATED', DT(2020, 1, 1, 1, 1)), ('CREATED', DT(2020, 1, 1, 1, 1)),
], ],
content='First level content', content=' First level content\n',
children=[ children=[
HL('Second level', HL('Second level',
props=[('ID', '01-simple-second-level-id')], props=[('ID', '01-simple-second-level-id')],
content='Second level content', content='\n Second level content\n',
children=[ children=[
HL('Third level', HL('Third level',
props=[('ID', '01-simple-third-level-id')], props=[('ID', '01-simple-third-level-id')],
content='Third level content') content='\n Third level content\n')
]) ])
]))) ])))
@ -44,3 +45,38 @@ class TestSerde(unittest.TestCase):
doc = loads(orig) doc = loads(orig)
self.assertEqual(dumps(doc), orig) self.assertEqual(dumps(doc), orig)
def test_markup_file_02(self):
with open(os.path.join(DIR, '02-markup.org')) as f:
doc = load(f)
ex = Dom(props=[('TITLE', '02-Markup'),
('DESCRIPTION', 'Simple org file to test markup'),
('TODO', 'TODO(t) PAUSED(p) | DONE(d)')],
children=(HL('First level',
props=[
('ID', '02-markup-first-level-id'),
('CREATED', DT(2020, 1, 1, 1, 1)),
],
content=[
SPAN(" This is a ", BOLD("bold phrase"),
"."),
SPAN(""),
SPAN(" This is a ",
VERBATIM("verbatim phrase"), "."),
SPAN(""),
SPAN(" This is a ", ITALIC("italic phrase"),
"."),
SPAN(""),
SPAN(" This is a ",
STRIKE("strike-through phrase"), "."),
SPAN(""),
SPAN(" This is a ",
UNDERLINED("underlined phrase"), "."),
SPAN(""),
SPAN(" This is a ", CODE("code phrase"),
"."),
SPAN(""),
])))
ex.assert_matches(self, doc)

View File

@ -2,6 +2,8 @@ import collections
import unittest import unittest
from datetime import datetime from datetime import datetime
from org_dom import get_raw_contents
def timestamp_to_datetime(ts): def timestamp_to_datetime(ts):
return datetime(ts.year, ts.month, ts.day, ts.hour, ts.minute) return datetime(ts.year, ts.month, ts.day, ts.hour, ts.minute)
@ -48,13 +50,13 @@ class HL:
self.children = children self.children = children
def assert_matches(self, test_case: unittest.TestCase, doc): def assert_matches(self, test_case: unittest.TestCase, doc):
test_case.assertEqual(self.title, doc['title']) test_case.assertEqual(self.title, doc.title)
# Check properties # Check properties
if self.props is None: if self.props is None:
test_case.assertEqual(len(doc['properties']), 0) test_case.assertEqual(len(doc.properties), 0)
else: else:
doc_props = doc['properties'] doc_props = doc.properties
test_case.assertEqual(len(doc_props), len(self.props)) test_case.assertEqual(len(doc_props), len(self.props))
for i, prop in enumerate(self.props): for i, prop in enumerate(self.props):
@ -63,15 +65,84 @@ class HL:
test_case.assertEqual( test_case.assertEqual(
timestamp_to_datetime(doc_props[i].value), prop[1]) timestamp_to_datetime(doc_props[i].value), prop[1])
# @TODO: Check properties if isinstance(self.content, str):
test_case.assertEqual(get_raw_contents(doc), self.content)
else:
test_case.assertEqual(len(doc.contents), len(self.content))
for i, content in enumerate(self.content):
test_case.assertEqual(get_raw_contents(doc.contents[i]),
content.to_raw())
# Check children # Check children
if self.children is None: if self.children is None:
test_case.assertEqual(len(doc['children']), 0) test_case.assertEqual(len(doc.children), 0)
else: else:
doc_headlines = doc['children'] doc_headlines = doc.children
test_case.assertEqual(len(doc_headlines), len(self.children), test_case.assertEqual(len(doc_headlines), len(self.children),
self.title) self.title)
for i, children in enumerate(self.children): for i, children in enumerate(self.children):
children.assert_matches(test_case, doc_headlines[i]) children.assert_matches(test_case, doc_headlines[i])
class SPAN:
def __init__(self, *kwargs):
self.contents = kwargs
def to_raw(self):
chunks = []
for section in self.contents:
if isinstance(section, str):
chunks.append(section)
else:
chunks.append(section.to_raw())
return ''.join(chunks)
class BOLD:
def __init__(self, text):
self.text = text
def to_raw(self):
return '*{}*'.format(self.text)
class CODE:
def __init__(self, text):
self.text = text
def to_raw(self):
return '~{}~'.format(self.text)
class ITALIC:
def __init__(self, text):
self.text = text
def to_raw(self):
return '/{}/'.format(self.text)
class STRIKE:
def __init__(self, text):
self.text = text
def to_raw(self):
return '+{}+'.format(self.text)
class UNDERLINED:
def __init__(self, text):
self.text = text
def to_raw(self):
return '_{}_'.format(self.text)
class VERBATIM:
def __init__(self, text):
self.text = text
def to_raw(self):
return '={}='.format(self.text)