1#!/usr/local/bin/python3.8 2# vim:fileencoding=utf-8 3 4 5__license__ = 'GPL v3' 6__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>' 7 8import unittest, numbers 9 10from calibre.ebooks.epub.cfi.parse import parser, cfi_sort_key, decode_cfi 11from polyglot.builtins import iteritems 12 13 14class Tests(unittest.TestCase): 15 16 def test_sorting(self): 17 null_offsets = (0, (0, 0), 0) 18 for path, key in [ 19 ('/1/2/3', ((1, 2, 3), null_offsets)), 20 ('/1[id]:34[yyyy]', ((1,), (0, (0, 0), 34))), 21 ('/1@1:2', ((1,), (0, (2, 1), 0))), 22 ('/1~1.2', ((1,), (1.2, (0, 0), 0))), 23 ]: 24 self.assertEqual(cfi_sort_key(path), key) 25 26 def test_parsing(self): 27 p = parser() 28 29 def step(x): 30 if isinstance(x, numbers.Integral): 31 return {'num': x} 32 return {'num':x[0], 'id':x[1]} 33 34 def s(*args): 35 return {'steps':list(map(step, args))} 36 37 def r(*args): 38 idx = args.index('!') 39 ans = s(*args[:idx]) 40 ans['redirect'] = s(*args[idx+1:]) 41 return ans 42 43 def o(*args): 44 ans = s(1) 45 step = ans['steps'][-1] 46 typ, val = args[:2] 47 step[{'@':'spatial_offset', '~':'temporal_offset', ':':'text_offset'}[typ]] = val 48 if len(args) == 4: 49 typ, val = args[2:] 50 step[{'@':'spatial_offset', '~':'temporal_offset'}[typ]] = val 51 return ans 52 53 def a(before=None, after=None, **params): 54 ans = o(':', 3) 55 step = ans['steps'][-1] 56 ta = {} 57 if before is not None: 58 ta['before'] = before 59 if after is not None: 60 ta['after'] = after 61 if params: 62 ta['params'] = {str(k):(v,) if isinstance(v, str) else v for k, v in iteritems(params)} 63 if ta: 64 step['text_assertion'] = ta 65 return ans 66 67 for raw, path, leftover in [ 68 # Test parsing of steps 69 ('/2', s(2), ''), 70 ('/2/3/4', s(2, 3, 4), ''), 71 ('/1/2[some^,^^id]/3', s(1, (2, 'some,^id'), 3), ''), 72 ('/1/2!/3/4', r(1, 2, '!', 3, 4), ''), 73 ('/1/2[id]!/3/4', r(1, (2, 'id'), '!', 3, 4), ''), 74 ('/1!/2[id]/3/4', r(1, '!', (2, 'id'), 3, 4), ''), 75 76 # Test parsing of offsets 77 ('/1~0', o('~', 0), ''), 78 ('/1~7', o('~', 7), ''), 79 ('/1~43.1', o('~', 43.1), ''), 80 ('/1~0.01', o('~', 0.01), ''), 81 ('/1~1.301', o('~', 1.301), ''), 82 ('/1@23:34.1', o('@', (23, 34.1)), ''), 83 ('/1~3@3.1:2.3', o('~', 3.0, '@', (3.1, 2.3)), ''), 84 ('/1:0', o(':', 0), ''), 85 ('/1:3', o(':', 3), ''), 86 87 # Test parsing of text assertions 88 ('/1:3[aa^,b]', a('aa,b'), ''), 89 ('/1:3[aa-b]', a('aa-b'), ''), 90 ('/1:3[aa^-b]', a('aa-b'), ''), 91 ('/1:3[aa-^--b]', a('aa---b'), ''), 92 ('/1:3[aa^,b,c1]', a('aa,b', 'c1'), ''), 93 ('/1:3[,aa^,b]', a(after='aa,b'), ''), 94 ('/1:3[;s=a]', a(s='a'), ''), 95 ('/1:3[a;s=a]', a('a', s='a'), ''), 96 ('/1:3[a;s=a^,b,c^;d;x=y]', a('a', s=('a,b', 'c;d'), x='y'), ''), 97 98 ]: 99 self.assertEqual(p.parse_path(raw), (path, leftover)) 100 101 def test_cfi_decode(self): 102 from calibre.ebooks.oeb.polish.parsing import parse 103 root = parse(''' 104<html> 105<head></head> 106<body id="body01"> 107 <p>…</p> 108 <p>…</p> 109 <p>…</p> 110 <p>…</p> 111 <p id="para05">xxx<em>yyy</em>0123456789</p> 112 <p>…</p> 113 <p>…</p> 114 <img id="svgimg" src="foo.svg" alt="…"/> 115 <p>…</p> 116 <p><span>hello</span><span>goodbye</span>text here<em>adieu</em>text there</p> 117 </body> 118</html> 119''', line_numbers=True, linenumber_attribute='data-lnum') 120 body = root[-1] 121 122 def test(cfi, expected): 123 self.assertIs(decode_cfi(root, cfi), expected) 124 125 for cfi in '/4 /4[body01] /900[body01] /2[body01]'.split(): 126 test(cfi, body) 127 128 for i in range(len(body)): 129 test('/4/{}'.format((i + 1)*2), body[i]) 130 131 p = body[4] 132 test('/4/999[para05]', p) 133 test('/4/999[para05]/2', p[0]) 134 135 136def find_tests(): 137 return unittest.TestLoader().loadTestsFromTestCase(Tests) 138 139 140if __name__ == '__main__': 141 unittest.TextTestRunner(verbosity=2).run(find_tests()) 142