| 1 | import json |
| 2 | import os |
| 3 | from pathlib import Path |
| 4 | import runpy |
| 5 | import shutil |
| 6 | import stat |
| 7 | import subprocess |
| 8 | from tempfile import TemporaryDirectory |
| 9 | import unittest |
| 10 | |
| 11 | from document_model import EXPORTER |
| 12 | |
| 13 | ROOT = Path(__file__).resolve().parent.parent |
| 14 | compare = runpy.run_path(str(ROOT / 'tools/verify-document.py'))['compare'] |
| 15 | |
| 16 | |
| 17 | class 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 | |
| 76 | if __name__ == '__main__': |
| 77 | unittest.main() |