Skip to content

Commit aa35c21

Browse files
author
Steve Canny
committed
add 'Open an OPC Package' acceptance test
1 parent 53455e4 commit aa35c21

File tree

5 files changed

+196
-0
lines changed

5 files changed

+196
-0
lines changed

features/open-package.feature

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,10 @@ Feature: Open an OPC package
22
In order to access the methods and properties on an OPC package
33
As an Open XML developer
44
I need to open an arbitrary package
5+
6+
@wip
7+
Scenario: Open a PowerPoint file
8+
Given a python-opc working environment
9+
When I open a PowerPoint file
10+
Then the expected package rels are loaded
11+
And the expected parts are loaded

features/steps/opc_steps.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,170 @@
88
# the MIT License: http://www.opensource.org/licenses/mit-license.php
99

1010
"""Acceptance test steps for python-opc."""
11+
12+
import hashlib
13+
import os
14+
15+
from behave import given, when, then
16+
17+
from opc import OpcPackage
18+
from opc.constants import CONTENT_TYPE as CT, RELATIONSHIP_TYPE as RT
19+
20+
21+
def absjoin(*paths):
22+
return os.path.abspath(os.path.join(*paths))
23+
24+
thisdir = os.path.split(__file__)[0]
25+
test_file_dir = absjoin(thisdir, '../../tests/test_files')
26+
basic_pptx_path = absjoin(test_file_dir, 'test.pptx')
27+
28+
29+
# given ====================================================
30+
31+
@given('a python-opc working environment')
32+
def step_given_python_opc_working_environment(context):
33+
pass
34+
35+
36+
# when =====================================================
37+
38+
@when('I open a PowerPoint file')
39+
def step_when_open_basic_pptx(context):
40+
context.pkg = OpcPackage.open(basic_pptx_path)
41+
42+
43+
# then =====================================================
44+
45+
@then('the expected package rels are loaded')
46+
def step_then_expected_pkg_rels_loaded(context):
47+
expected_rel_values = (
48+
('rId1', RT.OFFICE_DOCUMENT, False, '/ppt/presentation.xml'),
49+
('rId2', RT.THUMBNAIL, False, '/docProps/thumbnail.jpeg'),
50+
('rId3', RT.CORE_PROPERTIES, False, '/docProps/core.xml'),
51+
('rId4', RT.EXTENDED_PROPERTIES, False, '/docProps/app.xml'),
52+
)
53+
assert len(expected_rel_values) == len(context.pkg.rels)
54+
for rId, reltype, is_external, partname in expected_rel_values:
55+
rel = context.pkg.rels[rId]
56+
assert rel.rId == rId, "rId is '%s'" % rel.rId
57+
assert rel.reltype == reltype, "reltype is '%s'" % rel.reltype
58+
assert rel.is_external == is_external
59+
assert rel.target_part.partname == partname, (
60+
"target partname is '%s'" % rel.target_part.partname)
61+
62+
63+
@then('the expected parts are loaded')
64+
def step_then_expected_parts_are_loaded(context):
65+
expected_part_values = {
66+
'/docProps/app.xml': (
67+
CT.OFC_EXTENDED_PROPERTIES, 'e5a7552c35180b9796f2132d39bc0d208cf'
68+
'8761f', []
69+
),
70+
'/docProps/core.xml': (
71+
CT.OPC_CORE_PROPERTIES, '08c8ff0912231db740fa1277d8fa4ef175a306e'
72+
'4', []
73+
),
74+
'/docProps/thumbnail.jpeg': (
75+
CT.JPEG, '8a93420017d57f9c69f802639ee9791579b21af5', []
76+
),
77+
'/ppt/presentation.xml': (
78+
CT.PML_PRESENTATION_MAIN,
79+
'efa7bee0ac72464903a67a6744c1169035d52a54',
80+
[
81+
('rId1', RT.SLIDE_MASTER, False,
82+
'/ppt/slideMasters/slideMaster1.xml'),
83+
('rId2', RT.SLIDE, False, '/ppt/slides/slide1.xml'),
84+
('rId3', RT.PRINTER_SETTINGS, False,
85+
'/ppt/printerSettings/printerSettings1.bin'),
86+
('rId4', RT.PRES_PROPS, False, '/ppt/presProps.xml'),
87+
('rId5', RT.VIEW_PROPS, False, '/ppt/viewProps.xml'),
88+
('rId6', RT.THEME, False, '/ppt/theme/theme1.xml'),
89+
('rId7', RT.TABLE_STYLES, False, '/ppt/tableStyles.xml'),
90+
]
91+
),
92+
'/ppt/printerSettings/printerSettings1.bin': (
93+
CT.PML_PRINTER_SETTINGS, 'b0feb4cc107c9b2d135b1940560cf8f045ffb7'
94+
'46', []
95+
),
96+
'/ppt/presProps.xml': (
97+
CT.PML_PRES_PROPS, '7d4981fd742429e6b8cc99089575ac0ee7db5194', []
98+
),
99+
'/ppt/viewProps.xml': (
100+
CT.PML_VIEW_PROPS, '172a42a6be09d04eab61ae3d49eff5580a4be451', []
101+
),
102+
'/ppt/theme/theme1.xml': (
103+
CT.OFC_THEME, '9f362326d8dc050ab6eef7f17335094bd06da47e', []
104+
),
105+
'/ppt/tableStyles.xml': (
106+
CT.PML_TABLE_STYLES, '49bfd13ed02199b004bf0a019a596f127758d926',
107+
[]
108+
),
109+
'/ppt/slideMasters/slideMaster1.xml': (
110+
CT.PML_SLIDE_MASTER, 'be6fe53e199ef10259227a447e4ac9530803ecce',
111+
[
112+
('rId1', RT.SLIDE_LAYOUT, False,
113+
'/ppt/slideLayouts/slideLayout1.xml'),
114+
('rId2', RT.SLIDE_LAYOUT, False,
115+
'/ppt/slideLayouts/slideLayout2.xml'),
116+
('rId3', RT.SLIDE_LAYOUT, False,
117+
'/ppt/slideLayouts/slideLayout3.xml'),
118+
('rId4', RT.THEME, False, '/ppt/theme/theme1.xml'),
119+
],
120+
),
121+
'/ppt/slideLayouts/slideLayout1.xml': (
122+
CT.PML_SLIDE_LAYOUT, 'bcbeb908e22346fecda6be389759ca9ed068693c',
123+
[
124+
('rId1', RT.SLIDE_MASTER, False,
125+
'/ppt/slideMasters/slideMaster1.xml'),
126+
],
127+
),
128+
'/ppt/slideLayouts/slideLayout2.xml': (
129+
CT.PML_SLIDE_LAYOUT, '316d0fb0ce4c3560fa2ed4edc3becf2c4ce84b6b',
130+
[
131+
('rId1', RT.SLIDE_MASTER, False,
132+
'/ppt/slideMasters/slideMaster1.xml'),
133+
],
134+
),
135+
'/ppt/slideLayouts/slideLayout3.xml': (
136+
CT.PML_SLIDE_LAYOUT, '5b704e54c995b7d1bd7d24ef996a573676cc15ca',
137+
[
138+
('rId1', RT.SLIDE_MASTER, False,
139+
'/ppt/slideMasters/slideMaster1.xml'),
140+
],
141+
),
142+
'/ppt/slides/slide1.xml': (
143+
CT.PML_SLIDE, '1841b18f1191629c70b7176d8e210fa2ef079d85',
144+
[
145+
('rId1', RT.SLIDE_LAYOUT, False,
146+
'/ppt/slideLayouts/slideLayout1.xml'),
147+
('rId2', RT.HYPERLINK, True,
148+
'https://github.com/scanny/python-pptx'),
149+
]
150+
),
151+
}
152+
assert len(context.pkg.parts) == len(expected_part_values), (
153+
"len(context.pkg.parts) is %d" % len(context.pkg.parts))
154+
for part in context.pkg.parts:
155+
partname = part.partname
156+
content_type, sha1, exp_rel_vals = expected_part_values[partname]
157+
assert part.content_type == content_type, (
158+
"content_type for %s is '%s'" % (partname, part.content_type))
159+
blob_sha1 = hashlib.sha1(part.blob).hexdigest()
160+
assert blob_sha1 == sha1, ("SHA1 for %s is '%s'" %
161+
(partname, blob_sha1))
162+
assert len(part.rels) == len(exp_rel_vals), (
163+
"len(part.rels) for %s is %d" % (partname, len(part.rels)))
164+
for rId, reltype, is_external, target in exp_rel_vals:
165+
rel = part.rels[rId]
166+
assert rel.rId == rId, "rId is '%s'" % rel.rId
167+
assert rel.reltype == reltype, ("reltype for %s on %s is '%s'" %
168+
(rId, partname, rel.reltype))
169+
assert rel.is_external == is_external
170+
if rel.is_external:
171+
assert rel.target_ref == target, (
172+
"target_ref for %s on %s is '%s'" %
173+
(rId, partname, rel.target_ref))
174+
else:
175+
assert rel.target_part.partname == target, (
176+
"target partname for %s on %s is '%s'" %
177+
(rId, partname, rel.target_part.partname))

opc/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
# This module is part of python-opc and is released under the MIT License:
88
# http://www.opensource.org/licenses/mit-license.php
99

10+
from opc.package import OpcPackage # noqa
11+
1012
__version__ = '0.0.1d1'

opc/package.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# package.py
4+
#
5+
# Copyright (C) 2012, 2013 Steve Canny scanny@cisco.com
6+
#
7+
# This module is part of python-opc and is released under the MIT License:
8+
# http://www.opensource.org/licenses/mit-license.php
9+
10+
"""
11+
Provides an API for manipulating Open Packaging Convention (OPC) packages.
12+
"""
13+
14+
15+
class OpcPackage(object):
16+
"""
17+
Main API class for |python-opc|. A new instance is constructed by calling
18+
the :meth:`open` class method with a path to a package file or file-like
19+
object containing one.
20+
"""

tests/test_files/test.pptx

28.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)