1import json
2import os
3from pathlib import Path
4import runpy
5import shutil
6import stat
7import subprocess
8from tempfile import TemporaryDirectory
9import unittest
10
11from document_model import EXPORTER
12
13ROOT = Path(__file__).resolve().parent.parent
14compare = runpy.run_path(str(ROOT / 'tools/verify-document.py'))['compare']
15
16
17class ProtectedDocumentTest(unittest.TestCase):
18 def test_native_autofit_is_distinct_from_a_fixed_column_width(self):
19 import xml.etree.ElementTree as ET
20 fixture = ROOT / 'corpus/native-encrypted'
21 manifest = json.loads((fixture / 'manifest.json').read_text())
22 with TemporaryDirectory() as temporary:
23 root = Path(temporary)
24 password = root / 'password'
25 password.write_text(manifest['password'])
26 native = root / 'read'
27 shutil.copytree(fixture / 'cold-2010-4763/read', native)
28 compare(fixture / 'encrypted-01/notebook', native, password_file=password)
29 differences = json.loads((root / 'table-autofit.json').read_text())
30 self.assertEqual(len(differences), 1)
31 self.assertAlmostEqual(differences[0]['stored'], 38.61, places=4)
32 self.assertAlmostEqual(differences[0]['native'], 39.146141, places=4)
33 xml_path = native / 'page-000.xml'
34 xml = ET.parse(xml_path)
35 column = next(n for n in xml.getroot().iter() if n.tag.endswith('}Column'))
36 column.set('isLocked', 'true')
37 xml.write(xml_path, encoding='utf-8')
38 with self.assertRaisesRegex(AssertionError, 'Table column lock state differs'):
39 compare(fixture / 'encrypted-01/notebook', native, password_file=password)
40 column.attrib.pop('isLocked')
41 column.set('width', 'NaN')
42 xml.write(xml_path, encoding='utf-8')
43 with self.assertRaisesRegex(AssertionError, 'Invalid native table column width'):
44 compare(fixture / 'encrypted-01/notebook', native, password_file=password)
45
46 def test_native_page_formatting_and_all_attachment_boundaries(self):
47 fixture = ROOT / 'corpus/native-protected-boundaries'
48 manifest = json.loads((fixture / 'manifest.json').read_text())
49 with TemporaryDirectory() as temporary:
50 root = Path(temporary)
51 password = root / 'password'
52 password.write_text(manifest['password'])
53 native = root / 'read'
54 shutil.copytree(fixture / 'read', native)
55 compare(fixture / 'notebook', native, password_file=password)
56 output = root / 'export'
57 subprocess.run([EXPORTER, fixture / 'notebook/synthetic.one', output,
58 '--password-file', password], check=True, capture_output=True)
59 if os.name == 'posix':
60 self.assertEqual(stat.S_IMODE(output.stat().st_mode), 0o700)
61 password.write_text(manifest['password'] + '\n')
62 failed = root / 'failed'
63 result = subprocess.run([EXPORTER, fixture / 'notebook/synthetic.one', failed,
64 '--password-file', password], capture_output=True)
65 self.assertNotEqual(result.returncode, 0)
66 self.assertEqual(result.stdout, b'')
67 self.assertFalse(failed.exists())
68 self.assertNotIn(manifest['password'].encode(), result.stderr)
69 password.write_text('x' * 65537)
70 result = subprocess.run([EXPORTER, fixture / 'notebook/synthetic.one',
71 '--password-file', password], capture_output=True)
72 self.assertNotEqual(result.returncode, 0)
73 self.assertEqual(result.stdout, b'')
74
75
76if __name__ == '__main__':
77 unittest.main()