# encoding: utf-8 """ Test suite for docx.opc.package module """ from __future__ import absolute_import import pytest from docx.opc.constants import RELATIONSHIP_TYPE as RT from docx.opc.coreprops import CoreProperties from docx.opc.package import OpcPackage, Unmarshaller from docx.opc.packuri import PACKAGE_URI from docx.opc.part import Part from docx.opc.parts.coreprops import CorePropertiesPart from docx.opc.pkgreader import PackageReader from docx.opc.rel import _Relationship, Relationships from ..unitutil.mock import ( call, class_mock, instance_mock, loose_mock, method_mock, Mock, patch, PropertyMock, property_mock ) class DescribeOpcPackage(object): def it_can_open_a_pkg_file(self, PackageReader_, PartFactory_, Unmarshaller_): # mockery ---------------------- pkg_file = Mock(name='pkg_file') pkg_reader = PackageReader_.from_file.return_value # exercise --------------------- pkg = OpcPackage.open(pkg_file) # verify ----------------------- PackageReader_.from_file.assert_called_once_with(pkg_file) Unmarshaller_.unmarshal.assert_called_once_with(pkg_reader, pkg, PartFactory_) assert isinstance(pkg, OpcPackage) def it_initializes_its_rels_collection_on_first_reference( self, Relationships_): pkg = OpcPackage() rels = pkg.rels Relationships_.assert_called_once_with(PACKAGE_URI.baseURI) assert rels == Relationships_.return_value def it_can_add_a_relationship_to_a_part(self, pkg_with_rels_, rel_attrs_): reltype, target, rId = rel_attrs_ pkg = pkg_with_rels_ # exercise --------------------- pkg.load_rel(reltype, target, rId) # verify ----------------------- pkg._rels.add_relationship.assert_called_once_with( reltype, target, rId, False ) def it_can_establish_a_relationship_to_another_part( self, relate_to_part_fixture_): pkg, part_, reltype, rId = relate_to_part_fixture_ _rId = pkg.relate_to(part_, reltype) pkg.rels.get_or_add.assert_called_once_with(reltype, part_) assert _rId == rId def it_can_provide_a_list_of_the_parts_it_contains(self): # mockery ---------------------- parts = [Mock(name='part1'), Mock(name='part2')] pkg = OpcPackage() # verify ----------------------- with patch.object(OpcPackage, 'iter_parts', return_value=parts): assert pkg.parts == [parts[0], parts[1]] def it_can_iterate_over_parts_by_walking_rels_graph(self): # +----------+ +--------+ # | pkg_rels |-----> | part_1 | # +----------+ +--------+ # | | ^ # v v | # external +--------+ # | part_2 | # +--------+ part1, part2 = (Mock(name='part1'), Mock(name='part2')) part1.rels = { 1: Mock(name='rel1', is_external=False, target_part=part2) } part2.rels = { 1: Mock(name='rel2', is_external=False, target_part=part1) } pkg = OpcPackage() pkg._rels = { 1: Mock(name='rel3', is_external=False, target_part=part1), 2: Mock(name='rel4', is_external=True), } # verify ----------------------- assert part1 in pkg.iter_parts() assert part2 in pkg.iter_parts() assert len([p for p in pkg.iter_parts()]) == 2 def it_can_find_a_part_related_by_reltype(self, related_part_fixture_): pkg, reltype, related_part_ = related_part_fixture_ related_part = pkg.part_related_by(reltype) pkg.rels.part_with_reltype.assert_called_once_with(reltype) assert related_part is related_part_ def it_can_save_to_a_pkg_file( self, pkg_file_, PackageWriter_, parts, parts_): pkg = OpcPackage() pkg.save(pkg_file_) for part in parts_: part.before_marshal.assert_called_once_with() PackageWriter_.write.assert_called_once_with( pkg_file_, pkg._rels, parts_ ) def it_provides_access_to_the_core_properties(self, core_props_fixture): opc_package, core_properties_ = core_props_fixture core_properties = opc_package.core_properties assert core_properties is core_properties_ def it_provides_access_to_the_core_properties_part_to_help( self, core_props_part_fixture): opc_package, core_properties_part_ = core_props_part_fixture core_properties_part = opc_package._core_properties_part assert core_properties_part is core_properties_part_ def it_creates_a_default_core_props_part_if_none_present( self, default_core_props_fixture): opc_package, CorePropertiesPart_, core_properties_part_ = ( default_core_props_fixture ) core_properties_part = opc_package._core_properties_part CorePropertiesPart_.default.assert_called_once_with(opc_package) opc_package.relate_to.assert_called_once_with( core_properties_part_, RT.CORE_PROPERTIES ) assert core_properties_part is core_properties_part_ # fixtures --------------------------------------------- @pytest.fixture def core_props_fixture( self, _core_properties_part_prop_, core_properties_part_, core_properties_): opc_package = OpcPackage() _core_properties_part_prop_.return_value = core_properties_part_ core_properties_part_.core_properties = core_properties_ return opc_package, core_properties_ @pytest.fixture def core_props_part_fixture( self, part_related_by_, core_properties_part_): opc_package = OpcPackage() part_related_by_.return_value = core_properties_part_ return opc_package, core_properties_part_ @pytest.fixture def default_core_props_fixture( self, part_related_by_, CorePropertiesPart_, relate_to_, core_properties_part_): opc_package = OpcPackage() part_related_by_.side_effect = KeyError CorePropertiesPart_.default.return_value = core_properties_part_ return opc_package, CorePropertiesPart_, core_properties_part_ @pytest.fixture def relate_to_part_fixture_(self, request, pkg, rels_, reltype): rId = 'rId99' rel_ = instance_mock(request, _Relationship, name='rel_', rId=rId) rels_.get_or_add.return_value = rel_ pkg._rels = rels_ part_ = instance_mock(request, Part, name='part_') return pkg, part_, reltype, rId @pytest.fixture def related_part_fixture_(self, request, rels_, reltype): related_part_ = instance_mock(request, Part, name='related_part_') rels_.part_with_reltype.return_value = related_part_ pkg = OpcPackage() pkg._rels = rels_ return pkg, reltype, related_part_ # fixture components ----------------------------------- @pytest.fixture def CorePropertiesPart_(self, request): return class_mock(request, 'docx.opc.package.CorePropertiesPart') @pytest.fixture def core_properties_(self, request): return instance_mock(request, CoreProperties) @pytest.fixture def core_properties_part_(self, request): return instance_mock(request, CorePropertiesPart) @pytest.fixture def _core_properties_part_prop_(self, request): return property_mock(request, OpcPackage, '_core_properties_part') @pytest.fixture def PackageReader_(self, request): return class_mock(request, 'docx.opc.package.PackageReader') @pytest.fixture def PackageWriter_(self, request): return class_mock(request, 'docx.opc.package.PackageWriter') @pytest.fixture def PartFactory_(self, request): return class_mock(request, 'docx.opc.package.PartFactory') @pytest.fixture def part_related_by_(self, request): return method_mock(request, OpcPackage, 'part_related_by') @pytest.fixture def parts(self, request, parts_): """ Return a mock patching property OpcPackage.parts, reversing the patch after each use. """ _patch = patch.object( OpcPackage, 'parts', new_callable=PropertyMock, return_value=parts_ ) request.addfinalizer(_patch.stop) return _patch.start() @pytest.fixture def parts_(self, request): part_ = instance_mock(request, Part, name='part_') part_2_ = instance_mock(request, Part, name='part_2_') return [part_, part_2_] @pytest.fixture def pkg(self, request): return OpcPackage() @pytest.fixture def pkg_file_(self, request): return loose_mock(request) @pytest.fixture def pkg_with_rels_(self, request, rels_): pkg = OpcPackage() pkg._rels = rels_ return pkg @pytest.fixture def Relationships_(self, request): return class_mock(request, 'docx.opc.package.Relationships') @pytest.fixture def rel_attrs_(self, request): reltype = 'http://rel/type' target_ = instance_mock(request, Part, name='target_') rId = 'rId99' return reltype, target_, rId @pytest.fixture def relate_to_(self, request): return method_mock(request, OpcPackage, 'relate_to') @pytest.fixture def rels_(self, request): return instance_mock(request, Relationships) @pytest.fixture def reltype(self, request): return 'http://rel/type' @pytest.fixture def Unmarshaller_(self, request): return class_mock(request, 'docx.opc.package.Unmarshaller') class DescribeUnmarshaller(object): def it_can_unmarshal_from_a_pkg_reader( self, pkg_reader_, pkg_, part_factory_, _unmarshal_parts, _unmarshal_relationships, parts_dict_): # exercise --------------------- Unmarshaller.unmarshal(pkg_reader_, pkg_, part_factory_) # verify ----------------------- _unmarshal_parts.assert_called_once_with( pkg_reader_, pkg_, part_factory_ ) _unmarshal_relationships.assert_called_once_with( pkg_reader_, pkg_, parts_dict_ ) for part in parts_dict_.values(): part.after_unmarshal.assert_called_once_with() pkg_.after_unmarshal.assert_called_once_with() def it_can_unmarshal_parts( self, pkg_reader_, pkg_, part_factory_, parts_dict_, partnames_, content_types_, reltypes_, blobs_): # fixture ---------------------- partname_, partname_2_ = partnames_ content_type_, content_type_2_ = content_types_ reltype_, reltype_2_ = reltypes_ blob_, blob_2_ = blobs_ # exercise --------------------- parts = Unmarshaller._unmarshal_parts( pkg_reader_, pkg_, part_factory_ ) # verify ----------------------- assert ( part_factory_.call_args_list == [ call(partname_, content_type_, reltype_, blob_, pkg_), call(partname_2_, content_type_2_, reltype_2_, blob_2_, pkg_) ] ) assert parts == parts_dict_ def it_can_unmarshal_relationships(self): # test data -------------------- reltype = 'http://reltype' # mockery ---------------------- pkg_reader = Mock(name='pkg_reader') pkg_reader.iter_srels.return_value = ( ('/', Mock(name='srel1', rId='rId1', reltype=reltype, target_partname='partname1', is_external=False)), ('/', Mock(name='srel2', rId='rId2', reltype=reltype, target_ref='target_ref_1', is_external=True)), ('partname1', Mock(name='srel3', rId='rId3', reltype=reltype, target_partname='partname2', is_external=False)), ('partname2', Mock(name='srel4', rId='rId4', reltype=reltype, target_ref='target_ref_2', is_external=True)), ) pkg = Mock(name='pkg') parts = {} for num in range(1, 3): name = 'part%d' % num part = Mock(name=name) parts['partname%d' % num] = part pkg.attach_mock(part, name) # exercise --------------------- Unmarshaller._unmarshal_relationships(pkg_reader, pkg, parts) # verify ----------------------- expected_pkg_calls = [ call.load_rel(reltype, parts['partname1'], 'rId1', False), call.load_rel(reltype, 'target_ref_1', 'rId2', True), call.part1.load_rel(reltype, parts['partname2'], 'rId3', False), call.part2.load_rel(reltype, 'target_ref_2', 'rId4', True), ] assert pkg.mock_calls == expected_pkg_calls # fixtures --------------------------------------------- @pytest.fixture def blobs_(self, request): blob_ = loose_mock(request, spec=str, name='blob_') blob_2_ = loose_mock(request, spec=str, name='blob_2_') return blob_, blob_2_ @pytest.fixture def content_types_(self, request): content_type_ = loose_mock(request, spec=str, name='content_type_') content_type_2_ = loose_mock(request, spec=str, name='content_type_2_') return content_type_, content_type_2_ @pytest.fixture def part_factory_(self, request, parts_): part_factory_ = loose_mock(request, spec=Part) part_factory_.side_effect = parts_ return part_factory_ @pytest.fixture def partnames_(self, request): partname_ = loose_mock(request, spec=str, name='partname_') partname_2_ = loose_mock(request, spec=str, name='partname_2_') return partname_, partname_2_ @pytest.fixture def parts_(self, request): part_ = instance_mock(request, Part, name='part_') part_2_ = instance_mock(request, Part, name='part_2') return part_, part_2_ @pytest.fixture def parts_dict_(self, request, partnames_, parts_): partname_, partname_2_ = partnames_ part_, part_2_ = parts_ return {partname_: part_, partname_2_: part_2_} @pytest.fixture def pkg_(self, request): return instance_mock(request, OpcPackage) @pytest.fixture def pkg_reader_( self, request, partnames_, content_types_, reltypes_, blobs_): partname_, partname_2_ = partnames_ content_type_, content_type_2_ = content_types_ reltype_, reltype_2_ = reltypes_ blob_, blob_2_ = blobs_ iter_spart_items = ( (partname_, content_type_, reltype_, blob_), (partname_2_, content_type_2_, reltype_2_, blob_2_), ) pkg_reader_ = instance_mock(request, PackageReader) pkg_reader_.iter_sparts.return_value = iter_spart_items return pkg_reader_ @pytest.fixture def reltypes_(self, request): reltype_ = instance_mock(request, str, name='reltype_') reltype_2_ = instance_mock(request, str, name='reltype_2') return reltype_, reltype_2_ @pytest.fixture def _unmarshal_parts(self, request, parts_dict_): return method_mock( request, Unmarshaller, '_unmarshal_parts', return_value=parts_dict_ ) @pytest.fixture def _unmarshal_relationships(self, request): return method_mock(request, Unmarshaller, '_unmarshal_relationships')