Skip to content

Commit d815baf

Browse files
author
Steve Canny
committed
opc: refactor Describe_ContentTypeMap
* Extract fixture to clean main test and enable parameterized testing. * Clean up ordering of objects in unitdata/rels.py
1 parent 11c45e5 commit d815baf

File tree

3 files changed

+112
-70
lines changed

3 files changed

+112
-70
lines changed

tests/opc/test_pkgreader.py

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,23 @@
44
Test suite for opc.pkgreader module
55
"""
66

7+
from __future__ import absolute_import, print_function, unicode_literals
8+
79
import pytest
810

911
from mock import call, Mock, patch
1012

11-
from docx.opc.constants import RELATIONSHIP_TARGET_MODE as RTM
13+
from docx.opc.constants import (
14+
CONTENT_TYPE as CT, RELATIONSHIP_TARGET_MODE as RTM
15+
)
1216
from docx.opc.packuri import PackURI
1317
from docx.opc.phys_pkg import _ZipPkgReader
1418
from docx.opc.pkgreader import (
1519
_ContentTypeMap, PackageReader, _SerializedPart, _SerializedRelationship,
1620
_SerializedRelationships
1721
)
1822

23+
from .unitdata.types import a_Default, a_Types, an_Override
1924
from ..unitutil import (
2025
initializer_mock, class_mock, function_mock, instance_mock, loose_mock,
2126
method_mock
@@ -258,45 +263,13 @@ def _walk_phys_parts(self, request):
258263

259264
class Describe_ContentTypeMap(object):
260265

261-
def it_can_construct_from_types_xml(self, oxml_fromstring_):
262-
# test data --------------------
263-
content_types = (
264-
'app/vnd.type1', 'app/vnd.type2', 'app/vnd.type3',
265-
'app/vnd.type4',
266-
)
267-
content_types_xml = '<DontCare/>'
268-
extensions = ('rels', 'xml')
269-
exts = tuple(['.%s' % extension for extension in extensions])
270-
partnames = ('/part/name1.xml', '/part/name2.xml')
271-
# mockery ----------------------
272-
overrides = (
273-
Mock(name='override_elm_1', partname=partnames[0],
274-
content_type=content_types[0]),
275-
Mock(name='override_elm_2', partname=partnames[1],
276-
content_type=content_types[1]),
277-
)
278-
defaults = (
279-
Mock(name='default_elm_1', extension=extensions[0],
280-
content_type=content_types[2]),
281-
Mock(name='default_elm_2', extension=extensions[1],
282-
content_type=content_types[3]),
283-
)
284-
types_elm = Mock(
285-
name='types_elm', overrides=overrides, defaults=defaults
266+
def it_can_construct_from_ct_item_xml(self, from_xml_fixture):
267+
content_types_xml, expected_defaults, expected_overrides = (
268+
from_xml_fixture
286269
)
287-
oxml_fromstring_.return_value = types_elm
288-
# exercise ---------------------
289270
ct_map = _ContentTypeMap.from_xml(content_types_xml)
290-
# verify -----------------------
291-
expected_overrides = {
292-
partnames[0]: content_types[0], partnames[1]: content_types[1]
293-
}
294-
expected_defaults = {
295-
exts[0]: content_types[2], exts[1]: content_types[3]
296-
}
297-
oxml_fromstring_.assert_called_once_with(content_types_xml)
298-
assert ct_map._overrides == expected_overrides
299271
assert ct_map._defaults == expected_defaults
272+
assert ct_map._overrides == expected_overrides
300273

301274
def it_matches_overrides(self):
302275
# test data --------------------
@@ -328,8 +301,44 @@ def it_should_raise_on_key_not_instance_of_PackURI(self):
328301
# fixtures ---------------------------------------------
329302

330303
@pytest.fixture
331-
def oxml_fromstring_(self, request):
332-
return function_mock(request, 'docx.opc.pkgreader.oxml_fromstring')
304+
def from_xml_fixture(self):
305+
entries = (
306+
('Default', 'xml', CT.XML),
307+
# ('Default', 'PNG', CT.PNG),
308+
('Override', '/ppt/presentation.xml', CT.PML_PRESENTATION_MAIN),
309+
)
310+
content_types_xml = self._xml_from(entries)
311+
expected_defaults = {}
312+
expected_overrides = {}
313+
for entry in entries:
314+
if entry[0] == 'Default':
315+
ext = ('.%s' % entry[1]).lower()
316+
content_type = entry[2]
317+
expected_defaults[ext] = content_type
318+
elif entry[0] == 'Override':
319+
partname, content_type = entry[1:]
320+
expected_overrides[partname] = content_type
321+
return content_types_xml, expected_defaults, expected_overrides
322+
323+
def _xml_from(self, entries):
324+
"""
325+
Return XML for a [Content_Types].xml based on items in *entries*.
326+
"""
327+
types_bldr = a_Types().with_nsdecls()
328+
for entry in entries:
329+
if entry[0] == 'Default':
330+
ext, content_type = entry[1:]
331+
default_bldr = a_Default()
332+
default_bldr.with_Extension(ext)
333+
default_bldr.with_ContentType(content_type)
334+
types_bldr.with_child(default_bldr)
335+
elif entry[0] == 'Override':
336+
partname, content_type = entry[1:]
337+
override_bldr = an_Override()
338+
override_bldr.with_PartName(partname)
339+
override_bldr.with_ContentType(content_type)
340+
types_bldr.with_child(override_bldr)
341+
return types_bldr.xml()
333342

334343

335344
class Describe_SerializedPart(object):

tests/opc/unitdata/rels.py

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,21 @@
1313
from docx.opc.oxml import oxml_fromstring
1414

1515

16+
class BaseBuilder(object):
17+
"""
18+
Provides common behavior for all data builders.
19+
"""
20+
@property
21+
def element(self):
22+
"""Return element based on XML generated by builder"""
23+
return oxml_fromstring(self.xml)
24+
25+
def with_indent(self, indent):
26+
"""Add integer *indent* spaces at beginning of element XML"""
27+
self._indent = indent
28+
return self
29+
30+
1631
class RelationshipsBuilder(object):
1732
"""Builder class for test Relationships"""
1833
partname_tmpls = {
@@ -50,28 +65,6 @@ def build(self):
5065
return rels
5166

5267

53-
def a_rels():
54-
"""
55-
Return a PartBuilder instance.
56-
"""
57-
return RelationshipsBuilder()
58-
59-
60-
class BaseBuilder(object):
61-
"""
62-
Provides common behavior for all data builders.
63-
"""
64-
@property
65-
def element(self):
66-
"""Return element based on XML generated by builder"""
67-
return oxml_fromstring(self.xml)
68-
69-
def with_indent(self, indent):
70-
"""Add integer *indent* spaces at beginning of element XML"""
71-
self._indent = indent
72-
return self
73-
74-
7568
class CT_DefaultBuilder(BaseBuilder):
7669
"""
7770
Test data builder for CT_Default (Default) XML element that appears in
@@ -277,25 +270,20 @@ def xml(self):
277270

278271

279272
def a_Default():
280-
"""Return a CT_DefaultBuilder instance"""
281273
return CT_DefaultBuilder()
282274

283275

284-
def an_Override():
285-
"""Return a CT_OverrideBuilder instance"""
286-
return CT_OverrideBuilder()
287-
288-
289276
def a_Relationship():
290-
"""Return a CT_RelationshipBuilder instance"""
291277
return CT_RelationshipBuilder()
292278

293279

294280
def a_Relationships():
295-
"""Return a CT_RelationshipsBuilder instance"""
296281
return CT_RelationshipsBuilder()
297282

298283

299284
def a_Types():
300-
"""Return a CT_TypesBuilder instance"""
301285
return CT_TypesBuilder()
286+
287+
288+
def an_Override():
289+
return CT_OverrideBuilder()

tests/opc/unitdata/types.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# encoding: utf-8
2+
3+
"""
4+
XML test data builders for [Content_Types].xml elements
5+
"""
6+
7+
from __future__ import absolute_import, print_function, unicode_literals
8+
9+
from docx.opc.oxml import nsmap
10+
11+
from ...unitdata import BaseBuilder
12+
13+
14+
class CT_DefaultBuilder(BaseBuilder):
15+
__tag__ = 'Default'
16+
__nspfxs__ = ('ct',)
17+
__attrs__ = ('Extension', 'ContentType')
18+
19+
20+
class CT_OverrideBuilder(BaseBuilder):
21+
__tag__ = 'Override'
22+
__nspfxs__ = ('ct',)
23+
__attrs__ = ('PartName', 'ContentType')
24+
25+
26+
class CT_TypesBuilder(BaseBuilder):
27+
__tag__ = 'Types'
28+
__nspfxs__ = ('ct',)
29+
__attrs__ = ()
30+
31+
def with_nsdecls(self, *nspfxs):
32+
self._nsdecls = ' xmlns="%s"' % nsmap['ct']
33+
return self
34+
35+
36+
def a_Default():
37+
return CT_DefaultBuilder()
38+
39+
40+
def a_Types():
41+
return CT_TypesBuilder()
42+
43+
44+
def an_Override():
45+
return CT_OverrideBuilder()

0 commit comments

Comments
 (0)