Merge ~sylvain-pineau/plainbox/+git/packaging:fix-transition into plainbox:master
- Git
- lp:~sylvain-pineau/plainbox/+git/packaging
- fix-transition
- Merge into master
Status: | Superseded |
---|---|
Proposed branch: | ~sylvain-pineau/plainbox/+git/packaging:fix-transition |
Merge into: | plainbox:master |
Diff against target: |
81883 lines (+77900/-0) (has conflicts) 172 files modified
MANIFEST.in (+10/-0) PKG-INFO (+62/-0) debian/.git-dpm (+8/-0) debian/changelog (+257/-0) debian/clean (+5/-0) debian/compat (+1/-0) debian/control (+106/-0) debian/copyright (+207/-0) debian/patches/documentation-theme (+42/-0) debian/patches/series (+3/-0) debian/patches/silence-logging-failure (+37/-0) debian/patches/unvendorize (+2434/-0) debian/plainbox.manpages (+32/-0) debian/python3-plainbox-doc.doc-base (+9/-0) debian/python3-plainbox-doc.docs (+1/-0) debian/python3-plainbox.install (+2/-0) debian/python3-plainbox.links (+2/-0) debian/python3-plainbox.manpages (+2/-0) debian/python3-plainbox.preinst (+44/-0) debian/rules (+49/-0) debian/source/format (+1/-0) debian/source/options (+1/-0) debian/tests/control (+2/-0) debian/tests/unit-tests (+10/-0) debian/watch (+3/-0) docs/author/index.rst (+7/-0) docs/author/intro.rst (+190/-0) docs/author/provider-files.rst (+12/-0) docs/author/provider-namespaces.rst (+14/-0) docs/author/provider-template.rst (+44/-0) docs/author/providers.rst (+4/-0) docs/author/tutorial.rst (+177/-0) docs/author/whitelists.rst (+120/-0) docs/conf.py (+7/-0) docs/dev/old.rst (+63/-0) docs/dev/trusted-launcher.rst (+22/-0) docs/glossary.rst (+17/-0) docs/manpages/plainbox-dev-analyze.rst (+18/-0) docs/manpages/plainbox-exporter-units.rst (+36/-0) docs/manpages/plainbox-file-units.rst (+10/-0) docs/manpages/plainbox-job-units.rst (+6/-0) docs/manpages/plainbox-run.rst (+150/-0) docs/manpages/plainbox-test-plan-units.rst (+12/-0) docs/manpages/plainbox-trusted-launcher-1.rst (+10/-0) docs/usage.rst (+43/-0) plainbox.egg-info/PKG-INFO (+62/-0) plainbox.egg-info/SOURCES.txt (+479/-0) plainbox.egg-info/dependency_links.txt (+1/-0) plainbox.egg-info/entry_points.txt (+38/-0) plainbox.egg-info/requires.txt (+7/-0) plainbox.egg-info/top_level.txt (+1/-0) plainbox/__init__.py (+22/-0) plainbox/__main__.py (+32/-0) plainbox/_lazymod.py (+138/-0) plainbox/abc.py (+36/-0) plainbox/data/report/hardware-1_0.rng (+552/-0) plainbox/impl/applogic.py (+26/-0) plainbox/impl/buildsystems.py (+10/-0) plainbox/impl/censoREd.py (+90/-0) plainbox/impl/commands/__init__.py (+4/-0) plainbox/impl/commands/cmd_analyze.py (+14/-0) plainbox/impl/commands/cmd_checkbox.py (+12/-0) plainbox/impl/commands/inv_analyze.py (+52/-0) plainbox/impl/commands/inv_checkbox.py (+61/-0) plainbox/impl/commands/inv_run.py (+25/-0) plainbox/impl/commands/inv_special.py (+6/-0) plainbox/impl/commands/test_run.py (+30/-0) plainbox/impl/ctrl.py (+158/-0) plainbox/impl/depmgr.py (+6/-0) plainbox/impl/exporter/__init__.py (+18/-0) plainbox/impl/exporter/jinja2.py (+25/-0) plainbox/impl/exporter/rfc822.py (+48/-0) plainbox/impl/exporter/tar.py (+15/-0) plainbox/impl/exporter/test_hexr.py (+661/-0) plainbox/impl/exporter/test_html.py (+20/-0) plainbox/impl/exporter/test_init.py (+8/-0) plainbox/impl/exporter/test_rfc822.py (+47/-0) plainbox/impl/exporter/xlsx.py (+69/-0) plainbox/impl/highlevel.py (+18/-0) plainbox/impl/launcher.py (+106/-0) plainbox/impl/logging.py (+14/-0) plainbox/impl/providers/__init__.py (+21/-0) plainbox/impl/providers/exporters/data/checkbox.html (+314/-0) plainbox/impl/providers/exporters/data/checkbox.json (+16/-0) plainbox/impl/providers/exporters/data/hexr.xml (+161/-0) plainbox/impl/providers/exporters/data/junit.xml (+4/-0) plainbox/impl/providers/exporters/units/exporter.pxu (+23/-0) plainbox/impl/providers/stubbox/units/jobs/local.pxu (+9/-0) plainbox/impl/providers/stubbox/units/jobs/multilevel.pxu (+25/-0) plainbox/impl/providers/stubbox/units/jobs/representative.pxu (+12/-0) plainbox/impl/providers/stubbox/units/jobs/stub.pxu (+27/-0) plainbox/impl/providers/stubbox/whitelists/stub.whitelist (+23/-0) plainbox/impl/providers/stubbox/whitelists/stub1.whitelist (+7/-0) plainbox/impl/providers/stubbox/whitelists/stub2.whitelist (+7/-0) plainbox/impl/result.py (+105/-0) plainbox/impl/runner.py (+55/-0) plainbox/impl/secure/config.py (+12/-0) plainbox/impl/secure/launcher1.py (+18/-0) plainbox/impl/secure/providers/__init__.py (+21/-0) plainbox/impl/secure/providers/test_v1.py (+155/-0) plainbox/impl/secure/providers/v1.py (+288/-0) plainbox/impl/secure/qualifiers.py (+232/-0) plainbox/impl/secure/test_config.py (+6/-0) plainbox/impl/secure/test_launcher1.py (+53/-0) plainbox/impl/secure/test_qualifiers.py (+243/-0) plainbox/impl/session/assistant.py (+116/-0) plainbox/impl/session/jobs.py (+7/-0) plainbox/impl/session/manager.py (+72/-0) plainbox/impl/session/state.py (+53/-0) plainbox/impl/session/storage.py (+503/-0) plainbox/impl/session/test_manager.py (+12/-0) plainbox/impl/session/test_resume.py (+314/-0) plainbox/impl/session/test_state.py (+100/-0) plainbox/impl/session/test_storage.py (+93/-0) plainbox/impl/session/test_suspend.py (+220/-0) plainbox/impl/test_box.py (+103/-0) plainbox/impl/test_censoREd.py (+25/-0) plainbox/impl/test_ctrl.py (+181/-0) plainbox/impl/test_launcher.py (+34/-0) plainbox/impl/transport.py (+149/-0) plainbox/impl/unit/concrete_validators.py (+8/-0) plainbox/impl/unit/file.py (+4/-0) plainbox/impl/unit/job.py (+36/-0) plainbox/impl/unit/test_job.py (+27/-0) plainbox/impl/unit/test_template.py (+3/-0) plainbox/impl/unit/test_unit.py (+5/-0) plainbox/impl/unit/testplan.py (+6/-0) plainbox/impl/unit/unit.py (+35/-0) plainbox/impl/xparsers.py (+119/-0) plainbox/impl/xscanners.py (+4/-0) plainbox/provider_manager.py (+54/-0) plainbox/public.py (+47/-0) plainbox/test-data/html-exporter/with_both_certification_status.html (+290/-0) plainbox/test-data/html-exporter/with_certification_blocker.html (+262/-0) plainbox/test-data/html-exporter/with_certification_non_blocker.html (+260/-0) plainbox/test-data/html-exporter/without_certification_status.html (+243/-0) plainbox/test-data/xml-exporter/example-data-certification-status.json (+19045/-0) plainbox/test-data/xml-exporter/example-data.json (+19043/-0) plainbox/test-data/xml-exporter/example-data.xml (+15754/-0) plainbox/test-data/xml-exporter/test_dump_with_binary_attachment.json (+7/-0) plainbox/test-data/xml-exporter/test_dump_with_binary_attachment.xml (+21/-0) plainbox/test-data/xml-exporter/test_dump_with_comments.json (+13/-0) plainbox/test-data/xml-exporter/test_dump_with_comments.xml (+30/-0) plainbox/test-data/xml-exporter/test_dump_with_hardware_info.json (+10/-0) plainbox/test-data/xml-exporter/test_dump_with_hardware_info.xml (+22/-0) plainbox/test-data/xml-exporter/test_dump_with_io_log.json (+14/-0) plainbox/test-data/xml-exporter/test_dump_with_io_log.xml (+30/-0) plainbox/test-data/xml-exporter/test_dump_with_text_attachment.json (+7/-0) plainbox/test-data/xml-exporter/test_dump_with_text_attachment.xml (+21/-0) plainbox/test_provider_manager.py (+9/-0) plainbox/test_public.py (+36/-0) plainbox/vendor/__init__.py (+8/-0) plainbox/vendor/argparse/py32-argparse.py (+2372/-0) plainbox/vendor/argparse/py33-argparse.py (+2377/-0) plainbox/vendor/argparse/py34-argparse.py (+2384/-0) plainbox/vendor/enum.py (+676/-0) plainbox/vendor/extcmd/glibc.py (+339/-0) plainbox/vendor/funcsigs/__init__.py (+29/-0) plainbox/vendor/glibc.py (+987/-0) plainbox/vendor/mock.py (+32/-0) plainbox/vendor/phablet.py (+599/-0) plainbox/vendor/pyglibc/__init__.py (+45/-0) plainbox/vendor/pyglibc/_abc.py (+42/-0) plainbox/vendor/pyglibc/_pipe.py (+61/-0) plainbox/vendor/pyglibc/_pthread_sigmask.py (+242/-0) plainbox/vendor/pyglibc/_signalfd.py (+227/-0) plainbox/vendor/pyglibc/_subreaper.py (+167/-0) plainbox/vendor/pyglibc/select.py (+290/-0) plainbox/vendor/pyglibc/selectors.py (+459/-0) po/POTFILES.in (+39/-0) setup.cfg (+12/-0) setup.py (+26/-0) Conflict in MANIFEST.in Conflict in docs/author/index.rst Conflict in docs/author/intro.rst Conflict in docs/author/provider-files.rst Conflict in docs/author/provider-namespaces.rst Conflict in docs/author/provider-template.rst Conflict in docs/author/providers.rst Conflict in docs/conf.py Conflict in docs/dev/old.rst Conflict in docs/dev/trusted-launcher.rst Conflict in docs/glossary.rst Conflict in docs/manpages/plainbox-dev-analyze.rst Conflict in docs/manpages/plainbox-exporter-units.rst Conflict in docs/manpages/plainbox-file-units.rst Conflict in docs/manpages/plainbox-job-units.rst Conflict in docs/manpages/plainbox-run.rst Conflict in docs/manpages/plainbox-test-plan-units.rst Conflict in docs/manpages/plainbox-trusted-launcher-1.rst Conflict in docs/usage.rst Conflict in plainbox/__init__.py Conflict in plainbox/abc.py Conflict in plainbox/impl/applogic.py Conflict in plainbox/impl/buildsystems.py Conflict in plainbox/impl/commands/__init__.py Conflict in plainbox/impl/commands/cmd_analyze.py Conflict in plainbox/impl/commands/cmd_checkbox.py Conflict in plainbox/impl/commands/inv_analyze.py Conflict in plainbox/impl/commands/inv_checkbox.py Conflict in plainbox/impl/commands/inv_run.py Conflict in plainbox/impl/commands/inv_special.py Conflict in plainbox/impl/commands/test_run.py Conflict in plainbox/impl/ctrl.py Conflict in plainbox/impl/depmgr.py Conflict in plainbox/impl/exporter/__init__.py Conflict in plainbox/impl/exporter/jinja2.py Conflict in plainbox/impl/exporter/tar.py Conflict in plainbox/impl/exporter/test_html.py Conflict in plainbox/impl/exporter/test_init.py Conflict in plainbox/impl/exporter/xlsx.py Conflict in plainbox/impl/highlevel.py Conflict in plainbox/impl/launcher.py Conflict in plainbox/impl/logging.py Conflict in plainbox/impl/providers/__init__.py Conflict in plainbox/impl/providers/exporters/data/checkbox.html Conflict in plainbox/impl/providers/exporters/data/checkbox.json Conflict in plainbox/impl/providers/exporters/data/junit.xml Conflict in plainbox/impl/providers/exporters/units/exporter.pxu Conflict in plainbox/impl/providers/stubbox/units/jobs/representative.pxu Conflict in plainbox/impl/providers/stubbox/units/jobs/stub.pxu Conflict in plainbox/impl/result.py Conflict in plainbox/impl/runner.py Conflict in plainbox/impl/secure/config.py Conflict in plainbox/impl/secure/launcher1.py Conflict in plainbox/impl/secure/providers/__init__.py Conflict in plainbox/impl/secure/providers/test_v1.py Conflict in plainbox/impl/secure/providers/v1.py Conflict in plainbox/impl/secure/qualifiers.py Conflict in plainbox/impl/secure/test_config.py Conflict in plainbox/impl/secure/test_launcher1.py Conflict in plainbox/impl/secure/test_qualifiers.py Conflict in plainbox/impl/session/assistant.py Conflict in plainbox/impl/session/jobs.py Conflict in plainbox/impl/session/manager.py Conflict in plainbox/impl/session/state.py Conflict in plainbox/impl/session/storage.py Conflict in plainbox/impl/session/test_manager.py Conflict in plainbox/impl/session/test_resume.py Conflict in plainbox/impl/session/test_state.py Conflict in plainbox/impl/session/test_storage.py Conflict in plainbox/impl/session/test_suspend.py Conflict in plainbox/impl/test_box.py Conflict in plainbox/impl/test_ctrl.py Conflict in plainbox/impl/test_launcher.py Conflict in plainbox/impl/transport.py Conflict in plainbox/impl/unit/concrete_validators.py Conflict in plainbox/impl/unit/file.py Conflict in plainbox/impl/unit/job.py Conflict in plainbox/impl/unit/test_job.py Conflict in plainbox/impl/unit/test_template.py Conflict in plainbox/impl/unit/test_unit.py Conflict in plainbox/impl/unit/testplan.py Conflict in plainbox/impl/unit/unit.py Conflict in plainbox/impl/xparsers.py Conflict in plainbox/impl/xscanners.py Conflict in plainbox/provider_manager.py Conflict in plainbox/test-data/html-exporter/with_both_certification_status.html Conflict in plainbox/test-data/html-exporter/with_certification_blocker.html Conflict in plainbox/test-data/html-exporter/with_certification_non_blocker.html Conflict in plainbox/test-data/html-exporter/without_certification_status.html Conflict in plainbox/test_provider_manager.py Conflict in plainbox/vendor/__init__.py Conflict in plainbox/vendor/mock.py Conflict in po/POTFILES.in Conflict in setup.cfg Conflict in setup.py |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Checkbox Developers | Pending | ||
Review via email: mp+343976@code.launchpad.net |
This proposal has been superseded by a proposal from 2018-04-24.
Commit message
Description of the change
fix transition to checkbox-ng
Unmerged commits
- 25fa201... by Sylvain Pineau
-
Make plainbox a transitional package
- cdd25c1... by Sylvain Pineau
-
Update silence logging failure patch
- 4cf370b... by Sylvain Pineau
-
Update unvendorize patch (-funcsigs)
- b2de646... by PMR <pmr@pmr-lander>
-
Merge #333833 from ~kissiel/
plainbox/ +git/packaging: add-pycrypto- dep - 227148e... by Maciej Kisielewski
-
add python3-crypto dependency
Signed-off-by: Maciej Kisielewski <email address hidden>
- 90bce32... by Sylvain Pineau
-
change version to 0.38.0-1
"new upstream version" - c2d46ce... by Zygmunt Krynicki
-
Revert the documentation theme back to default
PlainBox uses a customized sphinx theme that includes additional
HTML to integrate with online comment service. This should not be
a part of the offline documentation package.
Origin: upstream
Forwarded: not-needed
Last-Update: 2015-07-21Patch-Name: documentation-theme
- 648f8eb... by Zygmunt Krynicki
-
Silence setup failure of the logging subsystem
The logging subsystem has a feature that displays two lines of warnings
if the per-user log file cannot be created. This leads to spurious
errors when plainbox is invoked from a build environment. Before a
better solution is found this warning is disabled as all of the
subsequent, relevant, logging messages are display either way.
Bug-Ubuntu: https://bugs.launchpad .net/checkbox/ +bug/1262898
Forwarded: yes
Last-Update: 2014-03-18Patch-Name: silence-
logging- failure - e112ce0... by Zygmunt Krynicki
-
Remove vendorized modules
This patch replaces plainbox.
vendor. {mock,funcsigs} with equivalent
imports from the standard python3.3 library. Upstream will stop
shipping those vendorized modules when support for python3.2 is no
longer required.
Upstream: not-needed
Last-Update: 2014-03-18Patch-Name: unvendorize
- 0b21cff... by Sylvain Pineau
-
record new upstream branch created by importing plainbox_
0.38.0. orig.tar. gz
Preview Diff
1 | diff --git a/MANIFEST.in b/MANIFEST.in |
2 | index 0eed38a..8a92fdb 100644 |
3 | --- a/MANIFEST.in |
4 | +++ b/MANIFEST.in |
5 | @@ -35,6 +35,12 @@ include plainbox/impl/providers/stubbox/po/*.pot |
6 | include plainbox/impl/providers/stubbox/po/POTFILES.in |
7 | include plainbox/impl/providers/stubbox/units/jobs/*.pxu |
8 | include plainbox/impl/providers/stubbox/units/testplans/*.pxu |
9 | +<<<<<<< MANIFEST.in |
10 | +======= |
11 | +include plainbox/impl/providers/stubbox/whitelists/*.whitelist |
12 | + |
13 | +include plainbox/vendor/argparse/py*-argparse.py |
14 | +>>>>>>> MANIFEST.in |
15 | |
16 | include plainbox/qml_shell/qml_shell.qml |
17 | |
18 | @@ -45,4 +51,8 @@ recursive-exclude daily-package-testing * |
19 | recursive-include contrib *.policy |
20 | recursive-include docs *.rst *.py *.html *.conf |
21 | recursive-include plainbox/data *.rng |
22 | +<<<<<<< MANIFEST.in |
23 | recursive-include plainbox/test-data *.json *.html *.tar.xz |
24 | +======= |
25 | +recursive-include plainbox/test-data *.json *.xml *.html |
26 | +>>>>>>> MANIFEST.in |
27 | diff --git a/PKG-INFO b/PKG-INFO |
28 | new file mode 100644 |
29 | index 0000000..fafbf3f |
30 | --- /dev/null |
31 | +++ b/PKG-INFO |
32 | @@ -0,0 +1,62 @@ |
33 | +Metadata-Version: 1.1 |
34 | +Name: plainbox |
35 | +Version: 0.38.0 |
36 | +Summary: Toolkit for software and hardware integration testing |
37 | +Home-page: https://launchpad.net/plainbox/ |
38 | +Author: Zygmunt Krynicki |
39 | +Author-email: zygmunt.krynicki@canonical.com |
40 | +License: GPLv3 |
41 | +Description: PlainBox |
42 | + ======== |
43 | + |
44 | + PlainBox is a toolkit consisting of python3 library, development tools, |
45 | + documentation and examples. It is targeted at developers working on testing or |
46 | + certification applications and authors creating tests for such applications. |
47 | + |
48 | + PlainBox can be used to both create simple and comprehensive test tools as well |
49 | + as to develop and execute test jobs and test scenarios. It was created as a |
50 | + refined and rewritten core of the CheckBox project. It has a well tested and |
51 | + documented core, small but active development community and a collection of |
52 | + associated projects that use it as a lower-level engine/back-end library. |
53 | + |
54 | + PlainBox has a novel approach to discovering (and probing) hardware and |
55 | + software that is extensible and not hardwired into the system. It allows test |
56 | + developers to express association between a particular test and the hardware, |
57 | + software and configuration constraints that must be met for the test to execute |
58 | + meaningfully. This feature, along with pluggable test definitions, makes |
59 | + PlainBox flexible and applicable to many diverse testing situations, ranging |
60 | + from mobile phones, traditional desktop computers, servers and up to testing |
61 | + "cloud" installations. |
62 | + |
63 | + External Documentation Links |
64 | + ============================ |
65 | + |
66 | + * `Using PlainBox <http://plainbox.readthedocs.org/en/latest/usage.html>`_ |
67 | + * `Hacking on PlainBox <http://plainbox.readthedocs.org/en/latest/dev/index.html>`_ |
68 | + * `Testing PlainBox <http://plainbox.readthedocs.org/en/latest/dev/intro.html#running-plainbox-tests>`_ |
69 | + |
70 | + Known Issues |
71 | + ============ |
72 | + |
73 | + https://bugs.launchpad.net/plainbox |
74 | + |
75 | +Platform: POSIX |
76 | +Classifier: Development Status :: 5 - Production/Stable |
77 | +Classifier: Environment :: Console |
78 | +Classifier: Environment :: Console :: Curses |
79 | +Classifier: Intended Audience :: Developers |
80 | +Classifier: Intended Audience :: Information Technology |
81 | +Classifier: Intended Audience :: Manufacturing |
82 | +Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) |
83 | +Classifier: Natural Language :: English |
84 | +Classifier: Natural Language :: Polish |
85 | +Classifier: Operating System :: POSIX |
86 | +Classifier: Operating System :: POSIX :: Linux |
87 | +Classifier: Programming Language :: Python :: 3.2 |
88 | +Classifier: Programming Language :: Python :: 3.3 |
89 | +Classifier: Programming Language :: Python :: 3.4 |
90 | +Classifier: Topic :: Software Development :: Libraries :: Python Modules |
91 | +Classifier: Topic :: Software Development :: Quality Assurance |
92 | +Classifier: Topic :: Software Development :: Testing |
93 | +Classifier: Topic :: System :: Benchmark |
94 | +Classifier: Topic :: Utilities |
95 | diff --git a/debian/.git-dpm b/debian/.git-dpm |
96 | new file mode 100644 |
97 | index 0000000..6322bd6 |
98 | --- /dev/null |
99 | +++ b/debian/.git-dpm |
100 | @@ -0,0 +1,8 @@ |
101 | +# see git-dpm(1) from git-dpm package |
102 | +c2d46ce5be7fb38b74c536e85865128af008d864 |
103 | +c2d46ce5be7fb38b74c536e85865128af008d864 |
104 | +8d70019e76acb28b201b59f0e0d578e15973723b |
105 | +8d70019e76acb28b201b59f0e0d578e15973723b |
106 | +plainbox_0.38.0.orig.tar.gz |
107 | +93dbb907b96d266b3a0e0c761cc975f10e2ae669 |
108 | +1491112 |
109 | diff --git a/debian/changelog b/debian/changelog |
110 | new file mode 100644 |
111 | index 0000000..ab529e2 |
112 | --- /dev/null |
113 | +++ b/debian/changelog |
114 | @@ -0,0 +1,257 @@ |
115 | +plainbox (0.38.0-1) UNRELEASED; urgency=medium |
116 | + |
117 | + [ Pierre Equoy ] |
118 | + * Open for development (remove this message before releasing) |
119 | + * "new upstream version" |
120 | + * "new upstream version" |
121 | + |
122 | + [ Sylvain Pineau ] |
123 | + * "new upstream version" |
124 | + * "new upstream version" |
125 | + * "new upstream version" |
126 | + |
127 | + [ Pierre Equoy ] |
128 | + * "new upstream version" |
129 | + * "new upstream version" |
130 | + * "new upstream version" |
131 | + |
132 | + [ Sylvain Pineau ] |
133 | + * "new upstream version" |
134 | + * "new upstream version" |
135 | + * "new upstream version" |
136 | + * "new upstream version" |
137 | + * "new upstream version" |
138 | + * "new upstream version" |
139 | + * "new upstream version" |
140 | + * "new upstream version" |
141 | + * "new upstream version" |
142 | + * "new upstream version" |
143 | + * "new upstream version" |
144 | + |
145 | + -- Sylvain Pineau <sylvain.pineau@canonical.com> Thu, 16 Nov 2017 11:39:52 +0100 |
146 | + |
147 | +plainbox (0.31-1) UNRELEASED; urgency=medium |
148 | + |
149 | + [ Ondřej Nový ] |
150 | + * Fixed homepage (https) |
151 | + * Fixed VCS URL (https) |
152 | + |
153 | + [ Sylvain Pineau ] |
154 | + * new upstream version |
155 | + |
156 | + -- Sylvain Pineau <sylvain.pineau@canonical.com> Tue, 04 Oct 2016 13:53:56 +0200 |
157 | + |
158 | +plainbox (0.25-1) unstable; urgency=medium |
159 | + |
160 | + * New upstream release |
161 | + * List of fixed bugs: https://launchpad.net/plainbox/+milestone/0.25 |
162 | + |
163 | + -- Sylvain Pineau <sylvain.pineau@canonical.com> Tue, 05 Jan 2016 17:26:37 +0100 |
164 | + |
165 | +plainbox (0.24-1) unstable; urgency=medium |
166 | + |
167 | + * New upstream release with multiple fixes and new features. |
168 | + * One important feature is the introduction of the SessionAssistant class. |
169 | + It allows Plainbox to simplify common testing scenarios. |
170 | + The assistant acts as a middle-man between the session manager and the |
171 | + application. |
172 | + It handles all currently known stages of the testing work-flow. |
173 | + * Add a dependency on python3 guacamole, padme, requests and tk. |
174 | + * Plainbox now supports a new way to express estimated durations that is much |
175 | + easier for humans to read and write. |
176 | + * Plainbox now supports an *after* job ordering constraint. This constraint |
177 | + is very similar to the existing *depends* constraint, except that the |
178 | + outcome of the referenced job is not important. In practical terms, even if |
179 | + one job runs and fails, another job that runs *after* it, will run. |
180 | + * Plainbox now allows more than one resource object to be used in a resource |
181 | + expression (e.g. the manifest resource with something else). |
182 | + * Plainbox ignores trailing garbage after EOF while reading IOLog zip. |
183 | + See https://bugs.python.org/issue24301. |
184 | + |
185 | + -- Sylvain Pineau <sylvain.pineau@canonical.com> Fri, 04 Dec 2015 15:44:14 +0100 |
186 | + |
187 | +plainbox (0.22.2-2.1) unstable; urgency=medium |
188 | + |
189 | + * Non-maintainer upload. |
190 | + * Fix "FTBFS: dh_clean: rm: cannot remove 'plainbox.egg-info': d/clean did |
191 | + miss the final '/*' to be recognized as directory. (Closes: #805677) |
192 | + * Also clean a mo file to allow build twice in a row |
193 | + |
194 | + -- Tobias Frost <tobi@debian.org> Sun, 22 Nov 2015 13:07:43 +0100 |
195 | + |
196 | +plainbox (0.22.2-2) unstable; urgency=medium |
197 | + |
198 | + * debian/patches: add a pile of patches that bring in cherry-picked or |
199 | + brand-new fixes for issues uncovered by python 3.5. |
200 | + |
201 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Wed, 02 Sep 2015 16:21:41 +0200 |
202 | + |
203 | +plainbox (0.22.2-1) unstable; urgency=medium |
204 | + |
205 | + * New upstream maintenance release |
206 | + * Use a temporary HOME to work around LP: #1478906 |
207 | + * Build i18n catalogs for the exporters and categories providers. |
208 | + * Move myself to Uploaders and set the checkbox-devel@lists.ubuntu.com |
209 | + mailing list as the Maintainer. |
210 | + * Remove duplicate dependency on python3-xlsxwriter (via suggests and |
211 | + depends) from python3-plainbox. |
212 | + * Remove XS-Testsuite: autopkgtest as recommend by lintian. |
213 | + * De-duplicate licenses in debian/copyright |
214 | + * Correct filename patterns for pyglibc/glibc files. |
215 | + |
216 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Tue, 28 Jul 2015 12:23:17 +0200 |
217 | + |
218 | +plainbox (0.22-1) unstable; urgency=medium |
219 | + |
220 | + * New upstream release (sorry for skipping 0.21) with multiple fixes and new |
221 | + features. |
222 | + * One important feature is the introduction of exporter units that allow |
223 | + test developers to put any customized report directly into the test |
224 | + provider package. This allows us to remove all association with Ubuntu or |
225 | + Canonical from the core plainbox package and make it more universal for |
226 | + Debian and other distributions. |
227 | + * debian/control: Drop dependency on python3-lxml (and the associated |
228 | + security issues). Upstream moved away from lxml and has adopted Jinja2 as |
229 | + a more flexible system for creating arbitrary text-based reports. |
230 | + * debian/control: Make the dependency on python3-xlsxwriter explicit as it |
231 | + is now more directly tested and not so much optional. |
232 | + * debian/patches/documentation-theme: refresh patch |
233 | + * debian/patches/fix-packaging-metadata-units: add a combined patch that |
234 | + addresses three Debian-affecting bugs that prevent providers from |
235 | + generating some of their dependencies. |
236 | + |
237 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Tue, 21 Jul 2015 12:22:03 +0200 |
238 | + |
239 | +plainbox (0.20-1) unstable; urgency=medium |
240 | + |
241 | + * Use the new pypi redirector |
242 | + * Correct debian/copyright paths |
243 | + * Correct debian/copyright license names |
244 | + * Update Standards-Version to 3.9.6 (no change required) |
245 | + * New upstream release |
246 | + * Add debian/copyright entry for sphinxarg |
247 | + * Remove stubbox so that it's not packaged / installed |
248 | + * Break checkbox-ng << 0.18 so that upgrades work (packages are bound by |
249 | + unstable API and this is the matching release) |
250 | + * Ship plainbox-qml-shell manual page along with python3-plainbox package |
251 | + * Tweak how i18n catalogs are built not to corrupt the tree |
252 | + * Build i18n catalog for the 'categories' provider |
253 | + * Update generic copyright to -2015 |
254 | + * Remove textland-specific copyright (it's now the same as the rest of |
255 | + plainbox), GPL-3 (not 3+) |
256 | + * Add license specific to vendorized python-morris |
257 | + * Add license specific to vendorized python-glibc |
258 | + * Wrap-and-sort everything |
259 | + |
260 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Wed, 04 Mar 2015 18:49:06 +0100 |
261 | + |
262 | +plainbox (0.5.4-1) unstable; urgency=medium |
263 | + |
264 | + * New upstream release |
265 | + * List of fixed bugs: |
266 | + https://launchpad.net/plainbox/+milestone/0.5.4 |
267 | + |
268 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Thu, 10 Apr 2014 22:31:50 +0200 |
269 | + |
270 | +plainbox (0.5.3-2) unstable; urgency=medium |
271 | + |
272 | + * debian/python3-plainbox.preinst: remove |
273 | + /usr/lib/python3/dist-packages/{data,testdata} on upgrades (if they are |
274 | + not symbolic links). This fixes upgrades from the previous version. |
275 | + |
276 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Tue, 08 Apr 2014 00:11:37 +0200 |
277 | + |
278 | +plainbox (0.5.3-1) unstable; urgency=medium |
279 | + |
280 | + * New upstream release |
281 | + * debian/control: make python3-plainbox, plainbox, plainbox-secure-policy |
282 | + and plainbox-insecure-policy all depend on one version on themselves. |
283 | + LP: #1298284 |
284 | + * debian/control: break python3-checkbox-ng << 0.3 |
285 | + * debian/control: drop build and runtime dependency on python3-requests |
286 | + * debian/copyright: add entry for new file (_shlex.py) |
287 | + * debian/copyright: move python license to dedicated license section (reused |
288 | + by three modules) per example 2 on http://dep.debian.net/deps/dep5/ |
289 | + * debian/rules: regenerate translation templates |
290 | + * debian/rules: move plainbox/data and plainbox/test-data to |
291 | + /usr/share/python3-plainbox/ and use symlinks to keep original directories |
292 | + available. |
293 | + * List of fixed bugs: |
294 | + https://launchpad.net/checkbox/+milestone/plainbox-0.5.2 |
295 | + https://launchpad.net/plainbox/+milestone/0.5.3 |
296 | + |
297 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Tue, 01 Apr 2014 01:36:20 +0200 |
298 | + |
299 | +plainbox (0.5.1-1) unstable; urgency=medium |
300 | + |
301 | + * New upstream release |
302 | + * debian/control: drop X-Python3-Version << 3.5 |
303 | + * debian/patches/disable-development-option: dropped, applied upstream |
304 | + * debian/copyright: associate vendorized copies of argparse with appropriate |
305 | + copyright section |
306 | + |
307 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Wed, 19 Mar 2014 00:08:12 +0100 |
308 | + |
309 | +plainbox (0.5~b2-1) unstable; urgency=medium |
310 | + |
311 | + * New upstream release. |
312 | + * debian/control: build-depend on python3-distutils-extra for translations |
313 | + * debian/control: add support for python3.4 |
314 | + * debian/control: drop build dependency on help2man, the new release has |
315 | + native manual pages |
316 | + * debian/rules: build, install and clean up after translations |
317 | + * debian/clean: clean *.egg-info and *.pot files since those get |
318 | + regenerated |
319 | + * debian/source/options: ignore changes to .po files since intltools-update |
320 | + keeps bumping the timestamp embedded in them |
321 | + * debian/copyright: add license section for plainbox/impl/_argparse.py |
322 | + * debian/copyright: add new copyright entries for textland |
323 | + * debian/patches: refresh and reorder without any semantic changes |
324 | + * debian/patches: add patch to revert documentation theme to defaults |
325 | + * debian/watch: add mangling for alpha releases |
326 | + * debian/python3-plainbox.manpages, debian/plainbox.manpages: use manual |
327 | + pages build with sphinx |
328 | + |
329 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Thu, 13 Mar 2014 09:45:06 +0100 |
330 | + |
331 | +plainbox (0.4-4) unstable; urgency=medium |
332 | + |
333 | + * Team upload. |
334 | + * Autopkgtest improvements: enable verbose output, use $ADTTMP and stop |
335 | + redirecting output to /dev/null. |
336 | + * Export NO_PNG_PKG_MANGLE=1 in debian/rules to disable PNG stripping |
337 | + when pkgbinarymangler is installed (it breaks the testsuite). |
338 | + |
339 | + -- Dmitry Shachnev <mitya57@gmail.com> Wed, 22 Jan 2014 18:08:40 +0400 |
340 | + |
341 | +plainbox (0.4-3) unstable; urgency=medium |
342 | + |
343 | + * debian/tests/unit-tests: actually fail the test suite if unit tests fail. |
344 | + Thanks to Michael Terry for the fix. LP:#1265853 |
345 | + |
346 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Fri, 03 Jan 2014 16:55:54 +0100 |
347 | + |
348 | +plainbox (0.4-2) unstable; urgency=medium |
349 | + |
350 | + * debian/tests/control: Fix autopackage tests not to install all (including |
351 | + conflicting) packages blindly LP:#1264985 |
352 | + |
353 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Thu, 02 Jan 2014 10:33:29 +0100 |
354 | + |
355 | +plainbox (0.4-1) unstable; urgency=medium |
356 | + |
357 | + * New upstream release |
358 | + * plainbox-insecure-policy.install, plainbox-secure-policy.install: adjust |
359 | + packaging to install the same policykit .policy files under their new |
360 | + names |
361 | + * debian/copyright: update all Canonical-owned code to GPL-3 (not GPL-3+) |
362 | + * debian/control: mark python3.4 as unsupported as python3-lxml does not |
363 | + support python3.4 yet |
364 | + |
365 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Tue, 24 Dec 2013 14:27:09 +0100 |
366 | + |
367 | +plainbox (0.4~b2-1) unstable; urgency=low |
368 | + |
369 | + * Initial release (Closes: #730568) |
370 | + |
371 | + -- Zygmunt Krynicki <zygmunt.krynicki@canonical.com> Thu, 28 Nov 2013 23:38:28 +0000 |
372 | diff --git a/debian/clean b/debian/clean |
373 | new file mode 100644 |
374 | index 0000000..8f5ba15 |
375 | --- /dev/null |
376 | +++ b/debian/clean |
377 | @@ -0,0 +1,5 @@ |
378 | +plainbox.egg-info/* |
379 | +plainbox/impl/providers/stubbox/po/stubbox.pot |
380 | +po/plainbox.pot |
381 | +plainbox/vendor/sphinxarg/LICENSE |
382 | +plainbox/impl/providers/manifest/build/mo/pl/LC_MESSAGES/plainbox-provider-manifest.mo |
383 | diff --git a/debian/compat b/debian/compat |
384 | new file mode 100644 |
385 | index 0000000..ec63514 |
386 | --- /dev/null |
387 | +++ b/debian/compat |
388 | @@ -0,0 +1 @@ |
389 | +9 |
390 | diff --git a/debian/control b/debian/control |
391 | new file mode 100644 |
392 | index 0000000..7f675ae |
393 | --- /dev/null |
394 | +++ b/debian/control |
395 | @@ -0,0 +1,106 @@ |
396 | +Source: plainbox |
397 | +Section: utils |
398 | +Priority: optional |
399 | +Maintainer: Checkbox Developers <checkbox-devel@lists.ubuntu.com> |
400 | +Uploaders: Sylvain Pineau <sylvain.pineau@canonical.com>, |
401 | + Zygmunt Krynicki <zygmunt.krynicki@canonical.com>, |
402 | + Debian Python Modules Team <python-modules-team@lists.alioth.debian.org> |
403 | +Build-Depends: debhelper (>= 9), |
404 | + dh-python, |
405 | + python3-all, |
406 | + python3-crypto, |
407 | + python3-distutils-extra, |
408 | + python3-docutils, |
409 | + python3-guacamole, |
410 | + python3-jinja2, |
411 | + python3-padme, |
412 | + python3-pkg-resources, |
413 | + python3-requests, |
414 | + python3-requests-oauthlib, |
415 | + python3-setuptools, |
416 | + python3-sphinx, |
417 | + python3-tk, |
418 | + python3-xlsxwriter |
419 | +Standards-Version: 3.9.6 |
420 | +X-Python3-Version: >= 3.2 |
421 | +Vcs-Git: https://anonscm.debian.org/git/python-modules/packages/plainbox.git |
422 | +Vcs-Browser: https://anonscm.debian.org/cgit/python-modules/packages/plainbox.git |
423 | +Homepage: https://launchpad.net/plainbox |
424 | + |
425 | +Package: plainbox |
426 | +Architecture: all |
427 | +Depends: checkbox-ng (>=1) |
428 | +Description: toolkit for software and hardware integration testing |
429 | + PlainBox is a toolkit consisting of python3 library, development tools, |
430 | + documentation and examples. It is targeted at developers working on testing or |
431 | + certification applications and authors creating tests for such applications. |
432 | + . |
433 | + PlainBox can be used to both create simple and comprehensive test tools as |
434 | + well as to develop and execute test jobs and test scenarios. It was created as |
435 | + a refined and rewritten core of the Checkbox project. It has a well tested and |
436 | + documented core, small but active development community and a collection of |
437 | + associated projects that use it as a lower-level engine/back-end library. |
438 | + . |
439 | + PlainBox has a novel approach to discovering (and probing) hardware and |
440 | + software that is extensible and not hardwired into the system. It allows test |
441 | + developers to express association between a particular test and the hardware, |
442 | + software and configuration constraints that must be met for the test to |
443 | + execute meaningfully. This feature, along with pluggable test definitions, |
444 | + makes plainbox flexible and applicable to many diverse testing situations, |
445 | + ranging from mobile phones, traditional desktop computers, servers and up to |
446 | + testing "cloud" installations. |
447 | + . |
448 | + This package contains the plainbox executable |
449 | + |
450 | +Package: python3-plainbox |
451 | +Architecture: all |
452 | +Section: python |
453 | +Depends: python3-checkbox-ng (>=1) |
454 | +Description: toolkit for software and hardware testing (python3 module) |
455 | + PlainBox is a toolkit consisting of python3 library, development tools, |
456 | + documentation and examples. It is targeted at developers working on testing or |
457 | + certification applications and authors creating tests for such applications. |
458 | + . |
459 | + PlainBox can be used to both create simple and comprehensive test tools as |
460 | + well as to develop and execute test jobs and test scenarios. It was created as |
461 | + a refined and rewritten core of the Checkbox project. It has a well tested and |
462 | + documented core, small but active development community and a collection of |
463 | + associated projects that use it as a lower-level engine/back-end library. |
464 | + . |
465 | + PlainBox has a novel approach to discovering (and probing) hardware and |
466 | + software that is extensible and not hardwired into the system. It allows test |
467 | + developers to express association between a particular test and the hardware, |
468 | + software and configuration constraints that must be met for the test to |
469 | + execute meaningfully. This feature, along with pluggable test definitions, |
470 | + makes plainbox flexible and applicable to many diverse testing situations, |
471 | + ranging from mobile phones, traditional desktop computers, servers and up to |
472 | + testing "cloud" installations. |
473 | + . |
474 | + This package contains the plainbox python3 library. |
475 | + |
476 | +Package: python3-plainbox-doc |
477 | +Architecture: all |
478 | +Section: doc |
479 | +Priority: extra |
480 | +Depends: ${misc:Depends}, ${sphinxdoc:Depends} |
481 | +Description: toolkit for software and hardware testing (documentation) |
482 | + PlainBox is a toolkit consisting of python3 library, development tools, |
483 | + documentation and examples. It is targeted at developers working on testing or |
484 | + certification applications and authors creating tests for such applications. |
485 | + . |
486 | + PlainBox can be used to both create simple and comprehensive test tools as |
487 | + well as to develop and execute test jobs and test scenarios. It was created as |
488 | + a refined and rewritten core of the Checkbox project. It has a well tested and |
489 | + documented core, small but active development community and a collection of |
490 | + associated projects that use it as a lower-level engine/back-end library. |
491 | + . |
492 | + PlainBox has a novel approach to discovering (and probing) hardware and |
493 | + software that is extensible and not hardwired into the system. It allows test |
494 | + developers to express association between a particular test and the hardware, |
495 | + software and configuration constraints that must be met for the test to |
496 | + execute meaningfully. This feature, along with pluggable test definitions, |
497 | + makes plainbox flexible and applicable to many diverse testing situations, |
498 | + ranging from mobile phones, traditional desktop computers, servers and up to |
499 | + testing "cloud" installations. |
500 | + . |
501 | + This package contains the documentation for the plainbox python3 library |
502 | diff --git a/debian/copyright b/debian/copyright |
503 | new file mode 100644 |
504 | index 0000000..5784a09 |
505 | --- /dev/null |
506 | +++ b/debian/copyright |
507 | @@ -0,0 +1,207 @@ |
508 | +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ |
509 | +Upstream-Name: plainbox |
510 | +Source: https://launchpad.net/checkbox |
511 | + |
512 | +Files: * |
513 | +Copyright: Copyright 2012-2015 Canonical Ltd. |
514 | +License: GPL-3 |
515 | + This program is free software: you can redistribute it and/or modify |
516 | + it under the terms of the GNU General Public License version 3, |
517 | + as published by the Free Software Foundation. |
518 | + . |
519 | + This program is distributed in the hope that it will be useful, |
520 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
521 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
522 | + GNU General Public License for more details. |
523 | + . |
524 | + You should have received a copy of the GNU General Public License |
525 | + along with this program. If not, see <http://www.gnu.org/licenses/>. |
526 | + . |
527 | + On Debian-based systems the full text of the GPL, version 3, can be found at |
528 | + /usr/share/common-licenses/GPL-3. |
529 | + |
530 | +Files: plainbox/vendor/extcmd/* |
531 | +Copyright: |
532 | + Copyright (c) 2010-2012 Linaro Limited |
533 | + Copyright (c) 2013 Canonical Ltd. |
534 | +License: GPL-3+ |
535 | + This program is free software: you can redistribute it and/or modify |
536 | + it under the terms of the GNU General Public License as published by |
537 | + the Free Software Foundation, either version 3 of the License, or |
538 | + (at your option) any later version. |
539 | + . |
540 | + This program is distributed in the hope that it will be useful, |
541 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
542 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
543 | + GNU General Public License for more details. |
544 | + . |
545 | + You should have received a copy of the GNU General Public License |
546 | + along with this program. If not, see <http://www.gnu.org/licenses/>. |
547 | + . |
548 | + On Debian-based systems the full text of the GPL, version 3, can be found at |
549 | + /usr/share/common-licenses/GPL-3. |
550 | + |
551 | +Files: plainbox/vendor/funcsigs/* |
552 | +Copyright: |
553 | + Copyright 2013 Aaron Iles |
554 | + Copyright 2001-2013 Python Software Foundation; |
555 | +License: Apache-2.0 |
556 | + Licensed under the Apache License, Version 2.0 (the "License"); |
557 | + you may not use this file except in compliance with the License. |
558 | + You may obtain a copy of the License at |
559 | + . |
560 | + http://www.apache.org/licenses/LICENSE-2.0 |
561 | + . |
562 | + Unless required by applicable law or agreed to in writing, software |
563 | + distributed under the License is distributed on an "AS IS" BASIS, |
564 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
565 | + See the License for the specific language governing permissions and |
566 | + limitations under the License. |
567 | + . |
568 | + On Debian-based systems the full text of the Apache, version 2.0, can be found |
569 | + at /usr/share/common-licenses/Apache-2.0. |
570 | + |
571 | +Files: plainbox/vendor/mock.py |
572 | +Copyright: Copyright (C) 2007-2012 Michael Foord & the mock team |
573 | +License: BSD-3-clause |
574 | + Redistribution and use in source and binary forms, with or without |
575 | + modification, are permitted provided that the following conditions are |
576 | + met: |
577 | + . |
578 | + . |
579 | + * Redistributions of source code must retain the above copyright |
580 | + notice, this list of conditions and the following disclaimer. |
581 | + . |
582 | + * Redistributions in binary form must reproduce the above |
583 | + copyright notice, this list of conditions and the following |
584 | + disclaimer in the documentation and/or other materials provided |
585 | + with the distribution. |
586 | + . |
587 | + * Neither the name of Michael Foord nor the name of Voidspace |
588 | + may be used to endorse or promote products derived from this |
589 | + software without specific prior written permission. |
590 | + . |
591 | + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
592 | + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
593 | + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
594 | + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
595 | + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
596 | + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
597 | + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
598 | + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
599 | + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
600 | + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
601 | + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
602 | + |
603 | +Files: plainbox/vendor/argparse/*.py |
604 | +Copyright: Steven J. Bethard <steven.bethard@gmail.com>. |
605 | +License: Python |
606 | + |
607 | +Files: plainbox/vendor/sphinxarg/* |
608 | +Copyright: Copyright (c) 2013 Alex Rudakov |
609 | +License: MIT |
610 | + The MIT License (MIT) |
611 | + . |
612 | + Permission is hereby granted, free of charge, to any person obtaining a copy of |
613 | + this software and associated documentation files (the "Software"), to deal in |
614 | + the Software without restriction, including without limitation the rights to |
615 | + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
616 | + the Software, and to permit persons to whom the Software is furnished to do so, |
617 | + subject to the following conditions: |
618 | + . |
619 | + The above copyright notice and this permission notice shall be included in all |
620 | + copies or substantial portions of the Software. |
621 | + . |
622 | + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
623 | + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
624 | + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
625 | + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
626 | + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
627 | + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
628 | + |
629 | +Files: plainbox/vendor/morris/* |
630 | +Copyright: Copyright (c) 2012-15 Canonical Ltd. |
631 | +License: LGPL-3+ |
632 | + |
633 | +Files: plainbox/vendor/glibc.py |
634 | +Copyright: Copyright (c) 2014 Canonical Ltd. |
635 | +License: LGPL-3+ |
636 | + |
637 | +Files: plainbox/vendor/pyglibc/* |
638 | +Copyright: Copyright (c) 2014 Canonical Ltd. |
639 | +License: LGPL-3+ |
640 | + |
641 | +Files: plainbox/impl/_argparse.py |
642 | +Copyright: Steven J. Bethard <steven.bethard@gmail.com>. |
643 | +License: Python |
644 | + |
645 | +Files: plainbox/impl/_shlex.py |
646 | +Copyright: |
647 | + Module and documentation by Eric S. Raymond, 21 Dec 1998 |
648 | + Input stacking and error message cleanup added by ESR, March 2000 |
649 | + push_source() and pop_source() made explicit by ESR, January 2001. |
650 | + Posix compliance, split(), string arguments, and |
651 | + iterator interface by Gustavo Niemeyer, April 2003. |
652 | +License: Python |
653 | + |
654 | +License: Python |
655 | + PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 |
656 | + -------------------------------------------- |
657 | + . |
658 | + 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), |
659 | + and the Individual or Organization ("Licensee") accessing and otherwise |
660 | + using this software ("Python") in source or binary form and its associated |
661 | + documentation. |
662 | + . |
663 | + 2. Subject to the terms and conditions of this License Agreement, PSF hereby |
664 | + grants Licensee a nonexclusive, royalty-free, world-wide license to |
665 | + reproduce, analyze, test, perform and/or display publicly, prepare |
666 | + derivative works, distribute, and otherwise use Python alone or in any |
667 | + derivative version, provided, however, that PSF's License Agreement and |
668 | + PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, |
669 | + 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Python Software |
670 | + Foundation; All Rights Reserved" are retained in Python alone or in any |
671 | + derivative version prepared by Licensee. |
672 | + . |
673 | + 3. In the event Licensee prepares a derivative work that is based on or |
674 | + incorporates Python or any part thereof, and wants to make the derivative |
675 | + work available to others as provided herein, then Licensee hereby agrees |
676 | + to include in any such work a brief summary of the changes made to Python. |
677 | + . |
678 | + 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES |
679 | + NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, |
680 | + BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR |
681 | + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT |
682 | + THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. |
683 | + . |
684 | + 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY |
685 | + INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF |
686 | + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE |
687 | + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. |
688 | + . |
689 | + 6. This License Agreement will automatically terminate upon a material breach |
690 | + of its terms and conditions. |
691 | + . |
692 | + 7. Nothing in this License Agreement shall be deemed to create any |
693 | + relationship of agency, partnership, or joint venture between PSF and |
694 | + Licensee. This License Agreement does not grant permission to use PSF |
695 | + trademarks or trade name in a trademark sense to endorse or promote |
696 | + products or services of Licensee, or any third party. |
697 | + . |
698 | + 8. By copying, installing or otherwise using Python, Licensee agrees to be |
699 | + bound by the terms and conditions of this License Agreement. |
700 | + |
701 | +License: LGPL-3+ |
702 | + This file is part of Morris. |
703 | + . |
704 | + Morris is free software: you can redistribute it and/or modify |
705 | + it under the terms of the GNU Lesser General Public License as published by |
706 | + the Free Software Foundation, either version 3 of the License. |
707 | + . |
708 | + Morris is distributed in the hope that it will be useful, |
709 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
710 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
711 | + GNU Lesser General Public License for more details. |
712 | + . |
713 | + You should have received a copy of the GNU Lesser General Public License |
714 | + along with Morris. If not, see <http://www.gnu.org/licenses/>. |
715 | diff --git a/debian/patches/documentation-theme b/debian/patches/documentation-theme |
716 | new file mode 100644 |
717 | index 0000000..d0306e7 |
718 | --- /dev/null |
719 | +++ b/debian/patches/documentation-theme |
720 | @@ -0,0 +1,42 @@ |
721 | +From c2d46ce5be7fb38b74c536e85865128af008d864 Mon Sep 17 00:00:00 2001 |
722 | +From: Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
723 | +Date: Thu, 8 Oct 2015 10:13:55 -0700 |
724 | +Subject: Revert the documentation theme back to default |
725 | + |
726 | + PlainBox uses a customized sphinx theme that includes additional |
727 | + HTML to integrate with online comment service. This should not be |
728 | + a part of the offline documentation package. |
729 | +Origin: upstream |
730 | +Forwarded: not-needed |
731 | +Last-Update: 2015-07-21 |
732 | + |
733 | +Patch-Name: documentation-theme |
734 | +--- |
735 | + docs/conf.py | 10 +--------- |
736 | + 1 file changed, 1 insertion(+), 9 deletions(-) |
737 | + |
738 | +diff --git a/docs/conf.py b/docs/conf.py |
739 | +index 07bd76e..ab2ab04 100644 |
740 | +--- a/docs/conf.py |
741 | ++++ b/docs/conf.py |
742 | +@@ -124,19 +124,11 @@ pygments_style = 'sphinx' |
743 | + # Use our custom theme. For now it only adds Disqus.com support but we may |
744 | + # customize it further later on. The theme is called 'plainbox' and has one |
745 | + # option which controls if disqus is active or not. |
746 | +-html_theme = 'plainbox' |
747 | ++html_theme = 'default' |
748 | + |
749 | + # Theme options are theme-specific and customize the look and feel of a theme |
750 | + # further. For a list of options available for each theme, see the |
751 | + # documentation. |
752 | +-# |
753 | +-# Due to the way disqus works, it's only going to work on |
754 | +-# plainbox.readthedocs.org so only use it if building for readthedocs. |
755 | +- |
756 | +-html_theme_options = { |
757 | +- 'show_disqus': 'true' if os.environ.get( |
758 | +- "READTHEDOCS", None) == 'True' else '' |
759 | +-} |
760 | + |
761 | + # Add any paths that contain custom themes here, relative to this directory. |
762 | + html_theme_path = ['_theme'] |
763 | diff --git a/debian/patches/series b/debian/patches/series |
764 | new file mode 100644 |
765 | index 0000000..5313c71 |
766 | --- /dev/null |
767 | +++ b/debian/patches/series |
768 | @@ -0,0 +1,3 @@ |
769 | +unvendorize |
770 | +silence-logging-failure |
771 | +documentation-theme |
772 | diff --git a/debian/patches/silence-logging-failure b/debian/patches/silence-logging-failure |
773 | new file mode 100644 |
774 | index 0000000..c61779b |
775 | --- /dev/null |
776 | +++ b/debian/patches/silence-logging-failure |
777 | @@ -0,0 +1,37 @@ |
778 | +From 648f8eb6ebd6465b85608915bb7ae1b021f5e809 Mon Sep 17 00:00:00 2001 |
779 | +From: Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
780 | +Date: Thu, 8 Oct 2015 10:13:54 -0700 |
781 | +Subject: Silence setup failure of the logging subsystem |
782 | + |
783 | + The logging subsystem has a feature that displays two lines of warnings |
784 | + if the per-user log file cannot be created. This leads to spurious |
785 | + errors when plainbox is invoked from a build environment. Before a |
786 | + better solution is found this warning is disabled as all of the |
787 | + subsequent, relevant, logging messages are display either way. |
788 | +Bug-Ubuntu: https://bugs.launchpad.net/checkbox/+bug/1262898 |
789 | +Forwarded: yes |
790 | +Last-Update: 2014-03-18 |
791 | + |
792 | +Patch-Name: silence-logging-failure |
793 | +--- |
794 | + plainbox/impl/logging.py | 5 ----- |
795 | + 1 file changed, 5 deletions(-) |
796 | + |
797 | +diff --git a/plainbox/impl/logging.py b/plainbox/impl/logging.py |
798 | +index 85fe7dd..6e66da0 100644 |
799 | +--- a/plainbox/impl/logging.py |
800 | ++++ b/plainbox/impl/logging.py |
801 | +@@ -95,12 +95,6 @@ class LoggingHelper: |
802 | + try: |
803 | + os.makedirs(self.log_dir, exist_ok=True) |
804 | + except OSError as error: |
805 | +- if not config_dict.get( |
806 | +- 'silence_eperm_on_logdir_warning', False): |
807 | +- logger.warning( |
808 | +- _("Unable to create log directory: %s"), self.log_dir) |
809 | +- logger.warning(_("Reason: %s. All logs will go to " |
810 | +- "console instead."), error) |
811 | + config_dict = self.DEFAULT_CONSOLE_ONLY_CONFIG |
812 | + # Apply the selected configuration. This overrides anything currently |
813 | + # defined for all of the logging subsystem in this python runtime |
814 | + |
815 | diff --git a/debian/patches/unvendorize b/debian/patches/unvendorize |
816 | new file mode 100644 |
817 | index 0000000..fce9d34 |
818 | --- /dev/null |
819 | +++ b/debian/patches/unvendorize |
820 | @@ -0,0 +1,2434 @@ |
821 | +From e112ce0246c305cba81c457e8aa3a7c40e87ab13 Mon Sep 17 00:00:00 2001 |
822 | +From: Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
823 | +Date: Thu, 8 Oct 2015 10:13:53 -0700 |
824 | +Subject: Remove vendorized modules |
825 | + |
826 | + This patch replaces plainbox.vendor.mock with equivalent imports from the |
827 | + standard python3.3 library. Upstream will stop shipping those vendorized |
828 | + modules when support for python3.2 is no longer required. |
829 | +Upstream: not-needed |
830 | +Last-Update: 2018-01-12 |
831 | + |
832 | +Patch-Name: unvendorize |
833 | +--- |
834 | + plainbox/vendor/__init__.py | 5 + |
835 | + plainbox/vendor/mock.py | 2398 +------------------------------------------ |
836 | + 2 files changed, 34 insertions(+), 2369 deletions(-) |
837 | + |
838 | +diff --git a/plainbox/vendor/__init__.py b/plainbox/vendor/__init__.py |
839 | +index d4ed746..188c398 100644 |
840 | +--- a/plainbox/vendor/__init__.py |
841 | ++++ b/plainbox/vendor/__init__.py |
842 | +@@ -25,4 +25,9 @@ This module contains external packages that were vendorized (shipped with a |
843 | + tree of another project) to simplify dependency management. There is no problem |
844 | + with expressing those dependencies at pypi level but it would be annoying to |
845 | + have to first package and introduce them to Ubuntu. |
846 | ++ |
847 | ++.. note:: |
848 | ++ The ``plainbox.vendor`` package is modified by Debian not to ship a copy of |
849 | ++ the ``unittest.mock`` and updated ``inspect`` modules that are already |
850 | ++ available in python3.3 |
851 | + """ |
852 | +diff --git a/plainbox/vendor/mock.py b/plainbox/vendor/mock.py |
853 | +index ca77df6..9916e39 100644 |
854 | +--- a/plainbox/vendor/mock.py |
855 | ++++ b/plainbox/vendor/mock.py |
856 | +@@ -1,2369 +1,29 @@ |
857 | +-# mock.py |
858 | +-# Test tools for mocking and patching. |
859 | +-# Copyright (C) 2007-2012 Michael Foord & the mock team |
860 | +-# E-mail: fuzzyman AT voidspace DOT org DOT uk |
861 | +- |
862 | +-# mock 1.0 |
863 | +-# http://www.voidspace.org.uk/python/mock/ |
864 | +- |
865 | +-# Released subject to the BSD License |
866 | +-# Please see http://www.voidspace.org.uk/python/license.shtml |
867 | +- |
868 | +-# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml |
869 | +-# Comments, suggestions and bug reports welcome. |
870 | +- |
871 | +- |
872 | +-__all__ = ( |
873 | +- 'Mock', |
874 | +- 'MagicMock', |
875 | +- 'patch', |
876 | +- 'sentinel', |
877 | +- 'DEFAULT', |
878 | +- 'ANY', |
879 | +- 'call', |
880 | +- 'create_autospec', |
881 | +- 'FILTER_DIR', |
882 | +- 'NonCallableMock', |
883 | +- 'NonCallableMagicMock', |
884 | +- 'mock_open', |
885 | +- 'PropertyMock', |
886 | +-) |
887 | +- |
888 | +- |
889 | +-__version__ = '1.0.1' |
890 | +- |
891 | +- |
892 | +-import pprint |
893 | +-import sys |
894 | +- |
895 | +-try: |
896 | +- import inspect |
897 | +-except ImportError: |
898 | +- # for alternative platforms that |
899 | +- # may not have inspect |
900 | +- inspect = None |
901 | +- |
902 | +-try: |
903 | +- from functools import wraps as original_wraps |
904 | +-except ImportError: |
905 | +- # Python 2.4 compatibility |
906 | +- def wraps(original): |
907 | +- def inner(f): |
908 | +- f.__name__ = original.__name__ |
909 | +- f.__doc__ = original.__doc__ |
910 | +- f.__module__ = original.__module__ |
911 | +- f.__wrapped__ = original |
912 | +- return f |
913 | +- return inner |
914 | +-else: |
915 | +- if sys.version_info[:2] >= (3, 3): |
916 | +- wraps = original_wraps |
917 | +- else: |
918 | +- def wraps(func): |
919 | +- def inner(f): |
920 | +- f = original_wraps(func)(f) |
921 | +- f.__wrapped__ = func |
922 | +- return f |
923 | +- return inner |
924 | +- |
925 | +-try: |
926 | +- unicode |
927 | +-except NameError: |
928 | +- # Python 3 |
929 | +- basestring = unicode = str |
930 | +- |
931 | +-try: |
932 | +- long |
933 | +-except NameError: |
934 | +- # Python 3 |
935 | +- long = int |
936 | +- |
937 | +-try: |
938 | +- BaseException |
939 | +-except NameError: |
940 | +- # Python 2.4 compatibility |
941 | +- BaseException = Exception |
942 | +- |
943 | +-try: |
944 | +- next |
945 | +-except NameError: |
946 | +- def next(obj): |
947 | +- return obj.next() |
948 | +- |
949 | +- |
950 | +-BaseExceptions = (BaseException,) |
951 | +-if 'java' in sys.platform: |
952 | +- # jython |
953 | +- import java |
954 | +- BaseExceptions = (BaseException, java.lang.Throwable) |
955 | +- |
956 | +-try: |
957 | +- _isidentifier = str.isidentifier |
958 | +-except AttributeError: |
959 | +- # Python 2.X |
960 | +- import keyword |
961 | +- import re |
962 | +- regex = re.compile(r'^[a-z_][a-z0-9_]*$', re.I) |
963 | +- def _isidentifier(string): |
964 | +- if string in keyword.kwlist: |
965 | +- return False |
966 | +- return regex.match(string) |
967 | +- |
968 | +- |
969 | +-inPy3k = sys.version_info[0] == 3 |
970 | +- |
971 | +-# Needed to work around Python 3 bug where use of "super" interferes with |
972 | +-# defining __class__ as a descriptor |
973 | +-_super = super |
974 | +- |
975 | +-self = 'im_self' |
976 | +-builtin = '__builtin__' |
977 | +-if inPy3k: |
978 | +- self = '__self__' |
979 | +- builtin = 'builtins' |
980 | +- |
981 | +-FILTER_DIR = True |
982 | +- |
983 | +- |
984 | +-def _is_instance_mock(obj): |
985 | +- # can't use isinstance on Mock objects because they override __class__ |
986 | +- # The base class for all mocks is NonCallableMock |
987 | +- return issubclass(type(obj), NonCallableMock) |
988 | +- |
989 | +- |
990 | +-def _is_exception(obj): |
991 | +- return ( |
992 | +- isinstance(obj, BaseExceptions) or |
993 | +- isinstance(obj, ClassTypes) and issubclass(obj, BaseExceptions) |
994 | +- ) |
995 | +- |
996 | +- |
997 | +-class _slotted(object): |
998 | +- __slots__ = ['a'] |
999 | +- |
1000 | +- |
1001 | +-DescriptorTypes = ( |
1002 | +- type(_slotted.a), |
1003 | +- property, |
1004 | +-) |
1005 | +- |
1006 | +- |
1007 | +-def _getsignature(func, skipfirst, instance=False): |
1008 | +- if inspect is None: |
1009 | +- raise ImportError('inspect module not available') |
1010 | +- |
1011 | +- if isinstance(func, ClassTypes) and not instance: |
1012 | +- try: |
1013 | +- func = func.__init__ |
1014 | +- except AttributeError: |
1015 | +- return |
1016 | +- skipfirst = True |
1017 | +- elif not isinstance(func, FunctionTypes): |
1018 | +- # for classes where instance is True we end up here too |
1019 | +- try: |
1020 | +- func = func.__call__ |
1021 | +- except AttributeError: |
1022 | +- return |
1023 | +- |
1024 | +- if inPy3k: |
1025 | +- try: |
1026 | +- argspec = inspect.getfullargspec(func) |
1027 | +- except TypeError: |
1028 | +- # C function / method, possibly inherited object().__init__ |
1029 | +- return |
1030 | +- regargs, varargs, varkw, defaults, kwonly, kwonlydef, ann = argspec |
1031 | +- else: |
1032 | +- try: |
1033 | +- regargs, varargs, varkwargs, defaults = inspect.getargspec(func) |
1034 | +- except TypeError: |
1035 | +- # C function / method, possibly inherited object().__init__ |
1036 | +- return |
1037 | +- |
1038 | +- # instance methods and classmethods need to lose the self argument |
1039 | +- if getattr(func, self, None) is not None: |
1040 | +- regargs = regargs[1:] |
1041 | +- if skipfirst: |
1042 | +- # this condition and the above one are never both True - why? |
1043 | +- regargs = regargs[1:] |
1044 | +- |
1045 | +- if inPy3k: |
1046 | +- signature = inspect.formatargspec( |
1047 | +- regargs, varargs, varkw, defaults, |
1048 | +- kwonly, kwonlydef, ann, formatvalue=lambda value: "") |
1049 | +- else: |
1050 | +- signature = inspect.formatargspec( |
1051 | +- regargs, varargs, varkwargs, defaults, |
1052 | +- formatvalue=lambda value: "") |
1053 | +- return signature[1:-1], func |
1054 | +- |
1055 | +- |
1056 | +-def _check_signature(func, mock, skipfirst, instance=False): |
1057 | +- if not _callable(func): |
1058 | +- return |
1059 | +- |
1060 | +- result = _getsignature(func, skipfirst, instance) |
1061 | +- if result is None: |
1062 | +- return |
1063 | +- signature, func = result |
1064 | +- |
1065 | +- # can't use self because "self" is common as an argument name |
1066 | +- # unfortunately even not in the first place |
1067 | +- src = "lambda _mock_self, %s: None" % signature |
1068 | +- checksig = eval(src, {}) |
1069 | +- _copy_func_details(func, checksig) |
1070 | +- type(mock)._mock_check_sig = checksig |
1071 | +- |
1072 | +- |
1073 | +-def _copy_func_details(func, funcopy): |
1074 | +- funcopy.__name__ = func.__name__ |
1075 | +- funcopy.__doc__ = func.__doc__ |
1076 | +- #funcopy.__dict__.update(func.__dict__) |
1077 | +- funcopy.__module__ = func.__module__ |
1078 | +- if not inPy3k: |
1079 | +- funcopy.func_defaults = func.func_defaults |
1080 | +- return |
1081 | +- funcopy.__defaults__ = func.__defaults__ |
1082 | +- funcopy.__kwdefaults__ = func.__kwdefaults__ |
1083 | +- |
1084 | +- |
1085 | +-def _callable(obj): |
1086 | +- if isinstance(obj, ClassTypes): |
1087 | +- return True |
1088 | +- if getattr(obj, '__call__', None) is not None: |
1089 | +- return True |
1090 | +- return False |
1091 | +- |
1092 | +- |
1093 | +-def _is_list(obj): |
1094 | +- # checks for list or tuples |
1095 | +- # XXXX badly named! |
1096 | +- return type(obj) in (list, tuple) |
1097 | +- |
1098 | +- |
1099 | +-def _instance_callable(obj): |
1100 | +- """Given an object, return True if the object is callable. |
1101 | +- For classes, return True if instances would be callable.""" |
1102 | +- if not isinstance(obj, ClassTypes): |
1103 | +- # already an instance |
1104 | +- return getattr(obj, '__call__', None) is not None |
1105 | +- |
1106 | +- klass = obj |
1107 | +- # uses __bases__ instead of __mro__ so that we work with old style classes |
1108 | +- if klass.__dict__.get('__call__') is not None: |
1109 | +- return True |
1110 | +- |
1111 | +- for base in klass.__bases__: |
1112 | +- if _instance_callable(base): |
1113 | +- return True |
1114 | +- return False |
1115 | +- |
1116 | +- |
1117 | +-def _set_signature(mock, original, instance=False): |
1118 | +- # creates a function with signature (*args, **kwargs) that delegates to a |
1119 | +- # mock. It still does signature checking by calling a lambda with the same |
1120 | +- # signature as the original. |
1121 | +- if not _callable(original): |
1122 | +- return |
1123 | +- |
1124 | +- skipfirst = isinstance(original, ClassTypes) |
1125 | +- result = _getsignature(original, skipfirst, instance) |
1126 | +- if result is None: |
1127 | +- # was a C function (e.g. object().__init__ ) that can't be mocked |
1128 | +- return |
1129 | +- |
1130 | +- signature, func = result |
1131 | +- |
1132 | +- src = "lambda %s: None" % signature |
1133 | +- checksig = eval(src, {}) |
1134 | +- _copy_func_details(func, checksig) |
1135 | +- |
1136 | +- name = original.__name__ |
1137 | +- if not _isidentifier(name): |
1138 | +- name = 'funcopy' |
1139 | +- context = {'_checksig_': checksig, 'mock': mock} |
1140 | +- src = """def %s(*args, **kwargs): |
1141 | +- _checksig_(*args, **kwargs) |
1142 | +- return mock(*args, **kwargs)""" % name |
1143 | +- exec (src, context) |
1144 | +- funcopy = context[name] |
1145 | +- _setup_func(funcopy, mock) |
1146 | +- return funcopy |
1147 | +- |
1148 | +- |
1149 | +-def _setup_func(funcopy, mock): |
1150 | +- funcopy.mock = mock |
1151 | +- |
1152 | +- # can't use isinstance with mocks |
1153 | +- if not _is_instance_mock(mock): |
1154 | +- return |
1155 | +- |
1156 | +- def assert_called_with(*args, **kwargs): |
1157 | +- return mock.assert_called_with(*args, **kwargs) |
1158 | +- def assert_called_once_with(*args, **kwargs): |
1159 | +- return mock.assert_called_once_with(*args, **kwargs) |
1160 | +- def assert_has_calls(*args, **kwargs): |
1161 | +- return mock.assert_has_calls(*args, **kwargs) |
1162 | +- def assert_any_call(*args, **kwargs): |
1163 | +- return mock.assert_any_call(*args, **kwargs) |
1164 | +- def reset_mock(): |
1165 | +- funcopy.method_calls = _CallList() |
1166 | +- funcopy.mock_calls = _CallList() |
1167 | +- mock.reset_mock() |
1168 | +- ret = funcopy.return_value |
1169 | +- if _is_instance_mock(ret) and not ret is mock: |
1170 | +- ret.reset_mock() |
1171 | +- |
1172 | +- funcopy.called = False |
1173 | +- funcopy.call_count = 0 |
1174 | +- funcopy.call_args = None |
1175 | +- funcopy.call_args_list = _CallList() |
1176 | +- funcopy.method_calls = _CallList() |
1177 | +- funcopy.mock_calls = _CallList() |
1178 | +- |
1179 | +- funcopy.return_value = mock.return_value |
1180 | +- funcopy.side_effect = mock.side_effect |
1181 | +- funcopy._mock_children = mock._mock_children |
1182 | +- |
1183 | +- funcopy.assert_called_with = assert_called_with |
1184 | +- funcopy.assert_called_once_with = assert_called_once_with |
1185 | +- funcopy.assert_has_calls = assert_has_calls |
1186 | +- funcopy.assert_any_call = assert_any_call |
1187 | +- funcopy.reset_mock = reset_mock |
1188 | +- |
1189 | +- mock._mock_delegate = funcopy |
1190 | +- |
1191 | +- |
1192 | +-def _is_magic(name): |
1193 | +- return '__%s__' % name[2:-2] == name |
1194 | +- |
1195 | +- |
1196 | +-class _SentinelObject(object): |
1197 | +- "A unique, named, sentinel object." |
1198 | +- def __init__(self, name): |
1199 | +- self.name = name |
1200 | +- |
1201 | +- def __repr__(self): |
1202 | +- return 'sentinel.%s' % self.name |
1203 | +- |
1204 | +- |
1205 | +-class _Sentinel(object): |
1206 | +- """Access attributes to return a named object, usable as a sentinel.""" |
1207 | +- def __init__(self): |
1208 | +- self._sentinels = {} |
1209 | +- |
1210 | +- def __getattr__(self, name): |
1211 | +- if name == '__bases__': |
1212 | +- # Without this help(mock) raises an exception |
1213 | +- raise AttributeError |
1214 | +- return self._sentinels.setdefault(name, _SentinelObject(name)) |
1215 | +- |
1216 | +- |
1217 | +-sentinel = _Sentinel() |
1218 | +- |
1219 | +-DEFAULT = sentinel.DEFAULT |
1220 | +-_missing = sentinel.MISSING |
1221 | +-_deleted = sentinel.DELETED |
1222 | +- |
1223 | +- |
1224 | +-class OldStyleClass: |
1225 | +- pass |
1226 | +-ClassType = type(OldStyleClass) |
1227 | +- |
1228 | +- |
1229 | +-def _copy(value): |
1230 | +- if type(value) in (dict, list, tuple, set): |
1231 | +- return type(value)(value) |
1232 | +- return value |
1233 | +- |
1234 | +- |
1235 | +-ClassTypes = (type,) |
1236 | +-if not inPy3k: |
1237 | +- ClassTypes = (type, ClassType) |
1238 | +- |
1239 | +-_allowed_names = set( |
1240 | +- [ |
1241 | +- 'return_value', '_mock_return_value', 'side_effect', |
1242 | +- '_mock_side_effect', '_mock_parent', '_mock_new_parent', |
1243 | +- '_mock_name', '_mock_new_name' |
1244 | +- ] |
1245 | +-) |
1246 | +- |
1247 | +- |
1248 | +-def _delegating_property(name): |
1249 | +- _allowed_names.add(name) |
1250 | +- _the_name = '_mock_' + name |
1251 | +- def _get(self, name=name, _the_name=_the_name): |
1252 | +- sig = self._mock_delegate |
1253 | +- if sig is None: |
1254 | +- return getattr(self, _the_name) |
1255 | +- return getattr(sig, name) |
1256 | +- def _set(self, value, name=name, _the_name=_the_name): |
1257 | +- sig = self._mock_delegate |
1258 | +- if sig is None: |
1259 | +- self.__dict__[_the_name] = value |
1260 | +- else: |
1261 | +- setattr(sig, name, value) |
1262 | +- |
1263 | +- return property(_get, _set) |
1264 | +- |
1265 | +- |
1266 | +- |
1267 | +-class _CallList(list): |
1268 | +- |
1269 | +- def __contains__(self, value): |
1270 | +- if not isinstance(value, list): |
1271 | +- return list.__contains__(self, value) |
1272 | +- len_value = len(value) |
1273 | +- len_self = len(self) |
1274 | +- if len_value > len_self: |
1275 | +- return False |
1276 | +- |
1277 | +- for i in range(0, len_self - len_value + 1): |
1278 | +- sub_list = self[i:i+len_value] |
1279 | +- if sub_list == value: |
1280 | +- return True |
1281 | +- return False |
1282 | +- |
1283 | +- def __repr__(self): |
1284 | +- return pprint.pformat(list(self)) |
1285 | +- |
1286 | +- |
1287 | +-def _check_and_set_parent(parent, value, name, new_name): |
1288 | +- if not _is_instance_mock(value): |
1289 | +- return False |
1290 | +- if ((value._mock_name or value._mock_new_name) or |
1291 | +- (value._mock_parent is not None) or |
1292 | +- (value._mock_new_parent is not None)): |
1293 | +- return False |
1294 | +- |
1295 | +- _parent = parent |
1296 | +- while _parent is not None: |
1297 | +- # setting a mock (value) as a child or return value of itself |
1298 | +- # should not modify the mock |
1299 | +- if _parent is value: |
1300 | +- return False |
1301 | +- _parent = _parent._mock_new_parent |
1302 | +- |
1303 | +- if new_name: |
1304 | +- value._mock_new_parent = parent |
1305 | +- value._mock_new_name = new_name |
1306 | +- if name: |
1307 | +- value._mock_parent = parent |
1308 | +- value._mock_name = name |
1309 | +- return True |
1310 | +- |
1311 | +- |
1312 | +- |
1313 | +-class Base(object): |
1314 | +- _mock_return_value = DEFAULT |
1315 | +- _mock_side_effect = None |
1316 | +- def __init__(self, *args, **kwargs): |
1317 | +- pass |
1318 | +- |
1319 | +- |
1320 | +- |
1321 | +-class NonCallableMock(Base): |
1322 | +- """A non-callable version of `Mock`""" |
1323 | +- |
1324 | +- def __new__(cls, *args, **kw): |
1325 | +- # every instance has its own class |
1326 | +- # so we can create magic methods on the |
1327 | +- # class without stomping on other mocks |
1328 | +- new = type(cls.__name__, (cls,), {'__doc__': cls.__doc__}) |
1329 | +- instance = object.__new__(new) |
1330 | +- return instance |
1331 | +- |
1332 | +- |
1333 | +- def __init__( |
1334 | +- self, spec=None, wraps=None, name=None, spec_set=None, |
1335 | +- parent=None, _spec_state=None, _new_name='', _new_parent=None, |
1336 | +- **kwargs |
1337 | +- ): |
1338 | +- if _new_parent is None: |
1339 | +- _new_parent = parent |
1340 | +- |
1341 | +- __dict__ = self.__dict__ |
1342 | +- __dict__['_mock_parent'] = parent |
1343 | +- __dict__['_mock_name'] = name |
1344 | +- __dict__['_mock_new_name'] = _new_name |
1345 | +- __dict__['_mock_new_parent'] = _new_parent |
1346 | +- |
1347 | +- if spec_set is not None: |
1348 | +- spec = spec_set |
1349 | +- spec_set = True |
1350 | +- |
1351 | +- self._mock_add_spec(spec, spec_set) |
1352 | +- |
1353 | +- __dict__['_mock_children'] = {} |
1354 | +- __dict__['_mock_wraps'] = wraps |
1355 | +- __dict__['_mock_delegate'] = None |
1356 | +- |
1357 | +- __dict__['_mock_called'] = False |
1358 | +- __dict__['_mock_call_args'] = None |
1359 | +- __dict__['_mock_call_count'] = 0 |
1360 | +- __dict__['_mock_call_args_list'] = _CallList() |
1361 | +- __dict__['_mock_mock_calls'] = _CallList() |
1362 | +- |
1363 | +- __dict__['method_calls'] = _CallList() |
1364 | +- |
1365 | +- if kwargs: |
1366 | +- self.configure_mock(**kwargs) |
1367 | +- |
1368 | +- _super(NonCallableMock, self).__init__( |
1369 | +- spec, wraps, name, spec_set, parent, |
1370 | +- _spec_state |
1371 | +- ) |
1372 | +- |
1373 | +- |
1374 | +- def attach_mock(self, mock, attribute): |
1375 | +- """ |
1376 | +- Attach a mock as an attribute of this one, replacing its name and |
1377 | +- parent. Calls to the attached mock will be recorded in the |
1378 | +- `method_calls` and `mock_calls` attributes of this one.""" |
1379 | +- mock._mock_parent = None |
1380 | +- mock._mock_new_parent = None |
1381 | +- mock._mock_name = '' |
1382 | +- mock._mock_new_name = None |
1383 | +- |
1384 | +- setattr(self, attribute, mock) |
1385 | +- |
1386 | +- |
1387 | +- def mock_add_spec(self, spec, spec_set=False): |
1388 | +- """Add a spec to a mock. `spec` can either be an object or a |
1389 | +- list of strings. Only attributes on the `spec` can be fetched as |
1390 | +- attributes from the mock. |
1391 | +- |
1392 | +- If `spec_set` is True then only attributes on the spec can be set.""" |
1393 | +- self._mock_add_spec(spec, spec_set) |
1394 | +- |
1395 | +- |
1396 | +- def _mock_add_spec(self, spec, spec_set): |
1397 | +- _spec_class = None |
1398 | +- |
1399 | +- if spec is not None and not _is_list(spec): |
1400 | +- if isinstance(spec, ClassTypes): |
1401 | +- _spec_class = spec |
1402 | +- else: |
1403 | +- _spec_class = _get_class(spec) |
1404 | +- |
1405 | +- spec = dir(spec) |
1406 | +- |
1407 | +- __dict__ = self.__dict__ |
1408 | +- __dict__['_spec_class'] = _spec_class |
1409 | +- __dict__['_spec_set'] = spec_set |
1410 | +- __dict__['_mock_methods'] = spec |
1411 | +- |
1412 | +- |
1413 | +- def __get_return_value(self): |
1414 | +- ret = self._mock_return_value |
1415 | +- if self._mock_delegate is not None: |
1416 | +- ret = self._mock_delegate.return_value |
1417 | +- |
1418 | +- if ret is DEFAULT: |
1419 | +- ret = self._get_child_mock( |
1420 | +- _new_parent=self, _new_name='()' |
1421 | +- ) |
1422 | +- self.return_value = ret |
1423 | +- return ret |
1424 | +- |
1425 | +- |
1426 | +- def __set_return_value(self, value): |
1427 | +- if self._mock_delegate is not None: |
1428 | +- self._mock_delegate.return_value = value |
1429 | +- else: |
1430 | +- self._mock_return_value = value |
1431 | +- _check_and_set_parent(self, value, None, '()') |
1432 | +- |
1433 | +- __return_value_doc = "The value to be returned when the mock is called." |
1434 | +- return_value = property(__get_return_value, __set_return_value, |
1435 | +- __return_value_doc) |
1436 | +- |
1437 | +- |
1438 | +- @property |
1439 | +- def __class__(self): |
1440 | +- if self._spec_class is None: |
1441 | +- return type(self) |
1442 | +- return self._spec_class |
1443 | +- |
1444 | +- called = _delegating_property('called') |
1445 | +- call_count = _delegating_property('call_count') |
1446 | +- call_args = _delegating_property('call_args') |
1447 | +- call_args_list = _delegating_property('call_args_list') |
1448 | +- mock_calls = _delegating_property('mock_calls') |
1449 | +- |
1450 | +- |
1451 | +- def __get_side_effect(self): |
1452 | +- sig = self._mock_delegate |
1453 | +- if sig is None: |
1454 | +- return self._mock_side_effect |
1455 | +- return sig.side_effect |
1456 | +- |
1457 | +- def __set_side_effect(self, value): |
1458 | +- value = _try_iter(value) |
1459 | +- sig = self._mock_delegate |
1460 | +- if sig is None: |
1461 | +- self._mock_side_effect = value |
1462 | +- else: |
1463 | +- sig.side_effect = value |
1464 | +- |
1465 | +- side_effect = property(__get_side_effect, __set_side_effect) |
1466 | +- |
1467 | +- |
1468 | +- def reset_mock(self): |
1469 | +- "Restore the mock object to its initial state." |
1470 | +- self.called = False |
1471 | +- self.call_args = None |
1472 | +- self.call_count = 0 |
1473 | +- self.mock_calls = _CallList() |
1474 | +- self.call_args_list = _CallList() |
1475 | +- self.method_calls = _CallList() |
1476 | +- |
1477 | +- for child in self._mock_children.values(): |
1478 | +- if isinstance(child, _SpecState): |
1479 | +- continue |
1480 | +- child.reset_mock() |
1481 | +- |
1482 | +- ret = self._mock_return_value |
1483 | +- if _is_instance_mock(ret) and ret is not self: |
1484 | +- ret.reset_mock() |
1485 | +- |
1486 | +- |
1487 | +- def configure_mock(self, **kwargs): |
1488 | +- """Set attributes on the mock through keyword arguments. |
1489 | +- |
1490 | +- Attributes plus return values and side effects can be set on child |
1491 | +- mocks using standard dot notation and unpacking a dictionary in the |
1492 | +- method call: |
1493 | +- |
1494 | +- >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} |
1495 | +- >>> mock.configure_mock(**attrs)""" |
1496 | +- for arg, val in sorted(kwargs.items(), |
1497 | +- # we sort on the number of dots so that |
1498 | +- # attributes are set before we set attributes on |
1499 | +- # attributes |
1500 | +- key=lambda entry: entry[0].count('.')): |
1501 | +- args = arg.split('.') |
1502 | +- final = args.pop() |
1503 | +- obj = self |
1504 | +- for entry in args: |
1505 | +- obj = getattr(obj, entry) |
1506 | +- setattr(obj, final, val) |
1507 | +- |
1508 | +- |
1509 | +- def __getattr__(self, name): |
1510 | +- if name == '_mock_methods': |
1511 | +- raise AttributeError(name) |
1512 | +- elif self._mock_methods is not None: |
1513 | +- if name not in self._mock_methods or name in _all_magics: |
1514 | +- raise AttributeError("Mock object has no attribute %r" % name) |
1515 | +- elif _is_magic(name): |
1516 | +- raise AttributeError(name) |
1517 | +- if name.startswith(('assert', 'assret')): |
1518 | +- raise AttributeError(name) |
1519 | +- |
1520 | +- result = self._mock_children.get(name) |
1521 | +- if result is _deleted: |
1522 | +- raise AttributeError(name) |
1523 | +- elif result is None: |
1524 | +- wraps = None |
1525 | +- if self._mock_wraps is not None: |
1526 | +- # XXXX should we get the attribute without triggering code |
1527 | +- # execution? |
1528 | +- wraps = getattr(self._mock_wraps, name) |
1529 | +- |
1530 | +- result = self._get_child_mock( |
1531 | +- parent=self, name=name, wraps=wraps, _new_name=name, |
1532 | +- _new_parent=self |
1533 | +- ) |
1534 | +- self._mock_children[name] = result |
1535 | +- |
1536 | +- elif isinstance(result, _SpecState): |
1537 | +- result = create_autospec( |
1538 | +- result.spec, result.spec_set, result.instance, |
1539 | +- result.parent, result.name |
1540 | +- ) |
1541 | +- self._mock_children[name] = result |
1542 | +- |
1543 | +- return result |
1544 | +- |
1545 | +- |
1546 | +- def __repr__(self): |
1547 | +- _name_list = [self._mock_new_name] |
1548 | +- _parent = self._mock_new_parent |
1549 | +- last = self |
1550 | +- |
1551 | +- dot = '.' |
1552 | +- if _name_list == ['()']: |
1553 | +- dot = '' |
1554 | +- seen = set() |
1555 | +- while _parent is not None: |
1556 | +- last = _parent |
1557 | +- |
1558 | +- _name_list.append(_parent._mock_new_name + dot) |
1559 | +- dot = '.' |
1560 | +- if _parent._mock_new_name == '()': |
1561 | +- dot = '' |
1562 | +- |
1563 | +- _parent = _parent._mock_new_parent |
1564 | +- |
1565 | +- # use ids here so as not to call __hash__ on the mocks |
1566 | +- if id(_parent) in seen: |
1567 | +- break |
1568 | +- seen.add(id(_parent)) |
1569 | +- |
1570 | +- _name_list = list(reversed(_name_list)) |
1571 | +- _first = last._mock_name or 'mock' |
1572 | +- if len(_name_list) > 1: |
1573 | +- if _name_list[1] not in ('()', '().'): |
1574 | +- _first += '.' |
1575 | +- _name_list[0] = _first |
1576 | +- name = ''.join(_name_list) |
1577 | +- |
1578 | +- name_string = '' |
1579 | +- if name not in ('mock', 'mock.'): |
1580 | +- name_string = ' name=%r' % name |
1581 | +- |
1582 | +- spec_string = '' |
1583 | +- if self._spec_class is not None: |
1584 | +- spec_string = ' spec=%r' |
1585 | +- if self._spec_set: |
1586 | +- spec_string = ' spec_set=%r' |
1587 | +- spec_string = spec_string % self._spec_class.__name__ |
1588 | +- return "<%s%s%s id='%s'>" % ( |
1589 | +- type(self).__name__, |
1590 | +- name_string, |
1591 | +- spec_string, |
1592 | +- id(self) |
1593 | +- ) |
1594 | +- |
1595 | +- |
1596 | +- def __dir__(self): |
1597 | +- """Filter the output of `dir(mock)` to only useful members. |
1598 | +- XXXX |
1599 | +- """ |
1600 | +- extras = self._mock_methods or [] |
1601 | +- from_type = dir(type(self)) |
1602 | +- from_dict = list(self.__dict__) |
1603 | +- |
1604 | +- if FILTER_DIR: |
1605 | +- from_type = [e for e in from_type if not e.startswith('_')] |
1606 | +- from_dict = [e for e in from_dict if not e.startswith('_') or |
1607 | +- _is_magic(e)] |
1608 | +- return sorted(set(extras + from_type + from_dict + |
1609 | +- list(self._mock_children))) |
1610 | +- |
1611 | +- |
1612 | +- def __setattr__(self, name, value): |
1613 | +- if name in _allowed_names: |
1614 | +- # property setters go through here |
1615 | +- return object.__setattr__(self, name, value) |
1616 | +- elif (self._spec_set and self._mock_methods is not None and |
1617 | +- name not in self._mock_methods and |
1618 | +- name not in self.__dict__): |
1619 | +- raise AttributeError("Mock object has no attribute '%s'" % name) |
1620 | +- elif name in _unsupported_magics: |
1621 | +- msg = 'Attempting to set unsupported magic method %r.' % name |
1622 | +- raise AttributeError(msg) |
1623 | +- elif name in _all_magics: |
1624 | +- if self._mock_methods is not None and name not in self._mock_methods: |
1625 | +- raise AttributeError("Mock object has no attribute '%s'" % name) |
1626 | +- |
1627 | +- if not _is_instance_mock(value): |
1628 | +- setattr(type(self), name, _get_method(name, value)) |
1629 | +- original = value |
1630 | +- value = lambda *args, **kw: original(self, *args, **kw) |
1631 | +- else: |
1632 | +- # only set _new_name and not name so that mock_calls is tracked |
1633 | +- # but not method calls |
1634 | +- _check_and_set_parent(self, value, None, name) |
1635 | +- setattr(type(self), name, value) |
1636 | +- self._mock_children[name] = value |
1637 | +- elif name == '__class__': |
1638 | +- self._spec_class = value |
1639 | +- return |
1640 | +- else: |
1641 | +- if _check_and_set_parent(self, value, name, name): |
1642 | +- self._mock_children[name] = value |
1643 | +- return object.__setattr__(self, name, value) |
1644 | +- |
1645 | +- |
1646 | +- def __delattr__(self, name): |
1647 | +- if name in _all_magics and name in type(self).__dict__: |
1648 | +- delattr(type(self), name) |
1649 | +- if name not in self.__dict__: |
1650 | +- # for magic methods that are still MagicProxy objects and |
1651 | +- # not set on the instance itself |
1652 | +- return |
1653 | +- |
1654 | +- if name in self.__dict__: |
1655 | +- object.__delattr__(self, name) |
1656 | +- |
1657 | +- obj = self._mock_children.get(name, _missing) |
1658 | +- if obj is _deleted: |
1659 | +- raise AttributeError(name) |
1660 | +- if obj is not _missing: |
1661 | +- del self._mock_children[name] |
1662 | +- self._mock_children[name] = _deleted |
1663 | +- |
1664 | +- |
1665 | +- |
1666 | +- def _format_mock_call_signature(self, args, kwargs): |
1667 | +- name = self._mock_name or 'mock' |
1668 | +- return _format_call_signature(name, args, kwargs) |
1669 | +- |
1670 | +- |
1671 | +- def _format_mock_failure_message(self, args, kwargs): |
1672 | +- message = 'Expected call: %s\nActual call: %s' |
1673 | +- expected_string = self._format_mock_call_signature(args, kwargs) |
1674 | +- call_args = self.call_args |
1675 | +- if len(call_args) == 3: |
1676 | +- call_args = call_args[1:] |
1677 | +- actual_string = self._format_mock_call_signature(*call_args) |
1678 | +- return message % (expected_string, actual_string) |
1679 | +- |
1680 | +- |
1681 | +- def assert_called_with(_mock_self, *args, **kwargs): |
1682 | +- """assert that the mock was called with the specified arguments. |
1683 | +- |
1684 | +- Raises an AssertionError if the args and keyword args passed in are |
1685 | +- different to the last call to the mock.""" |
1686 | +- self = _mock_self |
1687 | +- if self.call_args is None: |
1688 | +- expected = self._format_mock_call_signature(args, kwargs) |
1689 | +- raise AssertionError('Expected call: %s\nNot called' % (expected,)) |
1690 | +- |
1691 | +- if self.call_args != (args, kwargs): |
1692 | +- msg = self._format_mock_failure_message(args, kwargs) |
1693 | +- raise AssertionError(msg) |
1694 | +- |
1695 | +- |
1696 | +- def assert_called_once_with(_mock_self, *args, **kwargs): |
1697 | +- """assert that the mock was called exactly once and with the specified |
1698 | +- arguments.""" |
1699 | +- self = _mock_self |
1700 | +- if not self.call_count == 1: |
1701 | +- msg = ("Expected to be called once. Called %s times." % |
1702 | +- self.call_count) |
1703 | +- raise AssertionError(msg) |
1704 | +- return self.assert_called_with(*args, **kwargs) |
1705 | +- |
1706 | +- |
1707 | +- def assert_has_calls(self, calls, any_order=False): |
1708 | +- """assert the mock has been called with the specified calls. |
1709 | +- The `mock_calls` list is checked for the calls. |
1710 | +- |
1711 | +- If `any_order` is False (the default) then the calls must be |
1712 | +- sequential. There can be extra calls before or after the |
1713 | +- specified calls. |
1714 | +- |
1715 | +- If `any_order` is True then the calls can be in any order, but |
1716 | +- they must all appear in `mock_calls`.""" |
1717 | +- if not any_order: |
1718 | +- if calls not in self.mock_calls: |
1719 | +- raise AssertionError( |
1720 | +- 'Calls not found.\nExpected: %r\n' |
1721 | +- 'Actual: %r' % (calls, self.mock_calls) |
1722 | +- ) |
1723 | +- return |
1724 | +- |
1725 | +- all_calls = list(self.mock_calls) |
1726 | +- |
1727 | +- not_found = [] |
1728 | +- for kall in calls: |
1729 | +- try: |
1730 | +- all_calls.remove(kall) |
1731 | +- except ValueError: |
1732 | +- not_found.append(kall) |
1733 | +- if not_found: |
1734 | +- raise AssertionError( |
1735 | +- '%r not all found in call list' % (tuple(not_found),) |
1736 | +- ) |
1737 | +- |
1738 | +- |
1739 | +- def assert_any_call(self, *args, **kwargs): |
1740 | +- """assert the mock has been called with the specified arguments. |
1741 | +- |
1742 | +- The assert passes if the mock has *ever* been called, unlike |
1743 | +- `assert_called_with` and `assert_called_once_with` that only pass if |
1744 | +- the call is the most recent one.""" |
1745 | +- kall = call(*args, **kwargs) |
1746 | +- if kall not in self.call_args_list: |
1747 | +- expected_string = self._format_mock_call_signature(args, kwargs) |
1748 | +- raise AssertionError( |
1749 | +- '%s call not found' % expected_string |
1750 | +- ) |
1751 | +- |
1752 | +- |
1753 | +- def _get_child_mock(self, **kw): |
1754 | +- """Create the child mocks for attributes and return value. |
1755 | +- By default child mocks will be the same type as the parent. |
1756 | +- Subclasses of Mock may want to override this to customize the way |
1757 | +- child mocks are made. |
1758 | +- |
1759 | +- For non-callable mocks the callable variant will be used (rather than |
1760 | +- any custom subclass).""" |
1761 | +- _type = type(self) |
1762 | +- if not issubclass(_type, CallableMixin): |
1763 | +- if issubclass(_type, NonCallableMagicMock): |
1764 | +- klass = MagicMock |
1765 | +- elif issubclass(_type, NonCallableMock) : |
1766 | +- klass = Mock |
1767 | +- else: |
1768 | +- klass = _type.__mro__[1] |
1769 | +- return klass(**kw) |
1770 | +- |
1771 | +- |
1772 | +- |
1773 | +-def _try_iter(obj): |
1774 | +- if obj is None: |
1775 | +- return obj |
1776 | +- if _is_exception(obj): |
1777 | +- return obj |
1778 | +- if _callable(obj): |
1779 | +- return obj |
1780 | +- try: |
1781 | +- return iter(obj) |
1782 | +- except TypeError: |
1783 | +- # XXXX backwards compatibility |
1784 | +- # but this will blow up on first call - so maybe we should fail early? |
1785 | +- return obj |
1786 | +- |
1787 | +- |
1788 | +- |
1789 | +-class CallableMixin(Base): |
1790 | +- |
1791 | +- def __init__(self, spec=None, side_effect=None, return_value=DEFAULT, |
1792 | +- wraps=None, name=None, spec_set=None, parent=None, |
1793 | +- _spec_state=None, _new_name='', _new_parent=None, **kwargs): |
1794 | +- self.__dict__['_mock_return_value'] = return_value |
1795 | +- |
1796 | +- _super(CallableMixin, self).__init__( |
1797 | +- spec, wraps, name, spec_set, parent, |
1798 | +- _spec_state, _new_name, _new_parent, **kwargs |
1799 | +- ) |
1800 | +- |
1801 | +- self.side_effect = side_effect |
1802 | +- |
1803 | +- |
1804 | +- def _mock_check_sig(self, *args, **kwargs): |
1805 | +- # stub method that can be replaced with one with a specific signature |
1806 | +- pass |
1807 | +- |
1808 | +- |
1809 | +- def __call__(_mock_self, *args, **kwargs): |
1810 | +- # can't use self in-case a function / method we are mocking uses self |
1811 | +- # in the signature |
1812 | +- _mock_self._mock_check_sig(*args, **kwargs) |
1813 | +- return _mock_self._mock_call(*args, **kwargs) |
1814 | +- |
1815 | +- |
1816 | +- def _mock_call(_mock_self, *args, **kwargs): |
1817 | +- self = _mock_self |
1818 | +- self.called = True |
1819 | +- self.call_count += 1 |
1820 | +- self.call_args = _Call((args, kwargs), two=True) |
1821 | +- self.call_args_list.append(_Call((args, kwargs), two=True)) |
1822 | +- |
1823 | +- _new_name = self._mock_new_name |
1824 | +- _new_parent = self._mock_new_parent |
1825 | +- self.mock_calls.append(_Call(('', args, kwargs))) |
1826 | +- |
1827 | +- seen = set() |
1828 | +- skip_next_dot = _new_name == '()' |
1829 | +- do_method_calls = self._mock_parent is not None |
1830 | +- name = self._mock_name |
1831 | +- while _new_parent is not None: |
1832 | +- this_mock_call = _Call((_new_name, args, kwargs)) |
1833 | +- if _new_parent._mock_new_name: |
1834 | +- dot = '.' |
1835 | +- if skip_next_dot: |
1836 | +- dot = '' |
1837 | +- |
1838 | +- skip_next_dot = False |
1839 | +- if _new_parent._mock_new_name == '()': |
1840 | +- skip_next_dot = True |
1841 | +- |
1842 | +- _new_name = _new_parent._mock_new_name + dot + _new_name |
1843 | +- |
1844 | +- if do_method_calls: |
1845 | +- if _new_name == name: |
1846 | +- this_method_call = this_mock_call |
1847 | +- else: |
1848 | +- this_method_call = _Call((name, args, kwargs)) |
1849 | +- _new_parent.method_calls.append(this_method_call) |
1850 | +- |
1851 | +- do_method_calls = _new_parent._mock_parent is not None |
1852 | +- if do_method_calls: |
1853 | +- name = _new_parent._mock_name + '.' + name |
1854 | +- |
1855 | +- _new_parent.mock_calls.append(this_mock_call) |
1856 | +- _new_parent = _new_parent._mock_new_parent |
1857 | +- |
1858 | +- # use ids here so as not to call __hash__ on the mocks |
1859 | +- _new_parent_id = id(_new_parent) |
1860 | +- if _new_parent_id in seen: |
1861 | +- break |
1862 | +- seen.add(_new_parent_id) |
1863 | +- |
1864 | +- ret_val = DEFAULT |
1865 | +- effect = self.side_effect |
1866 | +- if effect is not None: |
1867 | +- if _is_exception(effect): |
1868 | +- raise effect |
1869 | +- |
1870 | +- if not _callable(effect): |
1871 | +- result = next(effect) |
1872 | +- if _is_exception(result): |
1873 | +- raise result |
1874 | +- return result |
1875 | +- |
1876 | +- ret_val = effect(*args, **kwargs) |
1877 | +- if ret_val is DEFAULT: |
1878 | +- ret_val = self.return_value |
1879 | +- |
1880 | +- if (self._mock_wraps is not None and |
1881 | +- self._mock_return_value is DEFAULT): |
1882 | +- return self._mock_wraps(*args, **kwargs) |
1883 | +- if ret_val is DEFAULT: |
1884 | +- ret_val = self.return_value |
1885 | +- return ret_val |
1886 | +- |
1887 | +- |
1888 | +- |
1889 | +-class Mock(CallableMixin, NonCallableMock): |
1890 | +- """ |
1891 | +- Create a new `Mock` object. `Mock` takes several optional arguments |
1892 | +- that specify the behaviour of the Mock object: |
1893 | +- |
1894 | +- * `spec`: This can be either a list of strings or an existing object (a |
1895 | +- class or instance) that acts as the specification for the mock object. If |
1896 | +- you pass in an object then a list of strings is formed by calling dir on |
1897 | +- the object (excluding unsupported magic attributes and methods). Accessing |
1898 | +- any attribute not in this list will raise an `AttributeError`. |
1899 | +- |
1900 | +- If `spec` is an object (rather than a list of strings) then |
1901 | +- `mock.__class__` returns the class of the spec object. This allows mocks |
1902 | +- to pass `isinstance` tests. |
1903 | +- |
1904 | +- * `spec_set`: A stricter variant of `spec`. If used, attempting to *set* |
1905 | +- or get an attribute on the mock that isn't on the object passed as |
1906 | +- `spec_set` will raise an `AttributeError`. |
1907 | +- |
1908 | +- * `side_effect`: A function to be called whenever the Mock is called. See |
1909 | +- the `side_effect` attribute. Useful for raising exceptions or |
1910 | +- dynamically changing return values. The function is called with the same |
1911 | +- arguments as the mock, and unless it returns `DEFAULT`, the return |
1912 | +- value of this function is used as the return value. |
1913 | +- |
1914 | +- Alternatively `side_effect` can be an exception class or instance. In |
1915 | +- this case the exception will be raised when the mock is called. |
1916 | +- |
1917 | +- If `side_effect` is an iterable then each call to the mock will return |
1918 | +- the next value from the iterable. If any of the members of the iterable |
1919 | +- are exceptions they will be raised instead of returned. |
1920 | +- |
1921 | +- * `return_value`: The value returned when the mock is called. By default |
1922 | +- this is a new Mock (created on first access). See the |
1923 | +- `return_value` attribute. |
1924 | +- |
1925 | +- * `wraps`: Item for the mock object to wrap. If `wraps` is not None then |
1926 | +- calling the Mock will pass the call through to the wrapped object |
1927 | +- (returning the real result). Attribute access on the mock will return a |
1928 | +- Mock object that wraps the corresponding attribute of the wrapped object |
1929 | +- (so attempting to access an attribute that doesn't exist will raise an |
1930 | +- `AttributeError`). |
1931 | +- |
1932 | +- If the mock has an explicit `return_value` set then calls are not passed |
1933 | +- to the wrapped object and the `return_value` is returned instead. |
1934 | +- |
1935 | +- * `name`: If the mock has a name then it will be used in the repr of the |
1936 | +- mock. This can be useful for debugging. The name is propagated to child |
1937 | +- mocks. |
1938 | +- |
1939 | +- Mocks can also be called with arbitrary keyword arguments. These will be |
1940 | +- used to set attributes on the mock after it is created. |
1941 | +- """ |
1942 | +- |
1943 | +- |
1944 | +- |
1945 | +-def _dot_lookup(thing, comp, import_path): |
1946 | +- try: |
1947 | +- return getattr(thing, comp) |
1948 | +- except AttributeError: |
1949 | +- __import__(import_path) |
1950 | +- return getattr(thing, comp) |
1951 | +- |
1952 | +- |
1953 | +-def _importer(target): |
1954 | +- components = target.split('.') |
1955 | +- import_path = components.pop(0) |
1956 | +- thing = __import__(import_path) |
1957 | +- |
1958 | +- for comp in components: |
1959 | +- import_path += ".%s" % comp |
1960 | +- thing = _dot_lookup(thing, comp, import_path) |
1961 | +- return thing |
1962 | +- |
1963 | +- |
1964 | +-def _is_started(patcher): |
1965 | +- # XXXX horrible |
1966 | +- return hasattr(patcher, 'is_local') |
1967 | +- |
1968 | +- |
1969 | +-class _patch(object): |
1970 | +- |
1971 | +- attribute_name = None |
1972 | +- _active_patches = set() |
1973 | +- |
1974 | +- def __init__( |
1975 | +- self, getter, attribute, new, spec, create, |
1976 | +- spec_set, autospec, new_callable, kwargs |
1977 | +- ): |
1978 | +- if new_callable is not None: |
1979 | +- if new is not DEFAULT: |
1980 | +- raise ValueError( |
1981 | +- "Cannot use 'new' and 'new_callable' together" |
1982 | +- ) |
1983 | +- if autospec is not None: |
1984 | +- raise ValueError( |
1985 | +- "Cannot use 'autospec' and 'new_callable' together" |
1986 | +- ) |
1987 | +- |
1988 | +- self.getter = getter |
1989 | +- self.attribute = attribute |
1990 | +- self.new = new |
1991 | +- self.new_callable = new_callable |
1992 | +- self.spec = spec |
1993 | +- self.create = create |
1994 | +- self.has_local = False |
1995 | +- self.spec_set = spec_set |
1996 | +- self.autospec = autospec |
1997 | +- self.kwargs = kwargs |
1998 | +- self.additional_patchers = [] |
1999 | +- |
2000 | +- |
2001 | +- def copy(self): |
2002 | +- patcher = _patch( |
2003 | +- self.getter, self.attribute, self.new, self.spec, |
2004 | +- self.create, self.spec_set, |
2005 | +- self.autospec, self.new_callable, self.kwargs |
2006 | +- ) |
2007 | +- patcher.attribute_name = self.attribute_name |
2008 | +- patcher.additional_patchers = [ |
2009 | +- p.copy() for p in self.additional_patchers |
2010 | +- ] |
2011 | +- return patcher |
2012 | +- |
2013 | +- |
2014 | +- def __call__(self, func): |
2015 | +- if isinstance(func, ClassTypes): |
2016 | +- return self.decorate_class(func) |
2017 | +- return self.decorate_callable(func) |
2018 | +- |
2019 | +- |
2020 | +- def decorate_class(self, klass): |
2021 | +- for attr in dir(klass): |
2022 | +- if not attr.startswith(patch.TEST_PREFIX): |
2023 | +- continue |
2024 | +- |
2025 | +- attr_value = getattr(klass, attr) |
2026 | +- if not hasattr(attr_value, "__call__"): |
2027 | +- continue |
2028 | +- |
2029 | +- patcher = self.copy() |
2030 | +- setattr(klass, attr, patcher(attr_value)) |
2031 | +- return klass |
2032 | +- |
2033 | +- |
2034 | +- def decorate_callable(self, func): |
2035 | +- if hasattr(func, 'patchings'): |
2036 | +- func.patchings.append(self) |
2037 | +- return func |
2038 | +- |
2039 | +- @wraps(func) |
2040 | +- def patched(*args, **keywargs): |
2041 | +- # don't use a with here (backwards compatability with Python 2.4) |
2042 | +- extra_args = [] |
2043 | +- entered_patchers = [] |
2044 | +- |
2045 | +- # can't use try...except...finally because of Python 2.4 |
2046 | +- # compatibility |
2047 | +- exc_info = tuple() |
2048 | +- try: |
2049 | +- try: |
2050 | +- for patching in patched.patchings: |
2051 | +- arg = patching.__enter__() |
2052 | +- entered_patchers.append(patching) |
2053 | +- if patching.attribute_name is not None: |
2054 | +- keywargs.update(arg) |
2055 | +- elif patching.new is DEFAULT: |
2056 | +- extra_args.append(arg) |
2057 | +- |
2058 | +- args += tuple(extra_args) |
2059 | +- return func(*args, **keywargs) |
2060 | +- except: |
2061 | +- if (patching not in entered_patchers and |
2062 | +- _is_started(patching)): |
2063 | +- # the patcher may have been started, but an exception |
2064 | +- # raised whilst entering one of its additional_patchers |
2065 | +- entered_patchers.append(patching) |
2066 | +- # Pass the exception to __exit__ |
2067 | +- exc_info = sys.exc_info() |
2068 | +- # re-raise the exception |
2069 | +- raise |
2070 | +- finally: |
2071 | +- for patching in reversed(entered_patchers): |
2072 | +- patching.__exit__(*exc_info) |
2073 | +- |
2074 | +- patched.patchings = [self] |
2075 | +- if hasattr(func, 'func_code'): |
2076 | +- # not in Python 3 |
2077 | +- patched.compat_co_firstlineno = getattr( |
2078 | +- func, "compat_co_firstlineno", |
2079 | +- func.func_code.co_firstlineno |
2080 | +- ) |
2081 | +- return patched |
2082 | +- |
2083 | +- |
2084 | +- def get_original(self): |
2085 | +- target = self.getter() |
2086 | +- name = self.attribute |
2087 | +- |
2088 | +- original = DEFAULT |
2089 | +- local = False |
2090 | +- |
2091 | +- try: |
2092 | +- original = target.__dict__[name] |
2093 | +- except (AttributeError, KeyError): |
2094 | +- original = getattr(target, name, DEFAULT) |
2095 | +- else: |
2096 | +- local = True |
2097 | +- |
2098 | +- if not self.create and original is DEFAULT: |
2099 | +- raise AttributeError( |
2100 | +- "%s does not have the attribute %r" % (target, name) |
2101 | +- ) |
2102 | +- return original, local |
2103 | +- |
2104 | +- |
2105 | +- def __enter__(self): |
2106 | +- """Perform the patch.""" |
2107 | +- new, spec, spec_set = self.new, self.spec, self.spec_set |
2108 | +- autospec, kwargs = self.autospec, self.kwargs |
2109 | +- new_callable = self.new_callable |
2110 | +- self.target = self.getter() |
2111 | +- |
2112 | +- # normalise False to None |
2113 | +- if spec is False: |
2114 | +- spec = None |
2115 | +- if spec_set is False: |
2116 | +- spec_set = None |
2117 | +- if autospec is False: |
2118 | +- autospec = None |
2119 | +- |
2120 | +- if spec is not None and autospec is not None: |
2121 | +- raise TypeError("Can't specify spec and autospec") |
2122 | +- if ((spec is not None or autospec is not None) and |
2123 | +- spec_set not in (True, None)): |
2124 | +- raise TypeError("Can't provide explicit spec_set *and* spec or autospec") |
2125 | +- |
2126 | +- original, local = self.get_original() |
2127 | +- |
2128 | +- if new is DEFAULT and autospec is None: |
2129 | +- inherit = False |
2130 | +- if spec is True: |
2131 | +- # set spec to the object we are replacing |
2132 | +- spec = original |
2133 | +- if spec_set is True: |
2134 | +- spec_set = original |
2135 | +- spec = None |
2136 | +- elif spec is not None: |
2137 | +- if spec_set is True: |
2138 | +- spec_set = spec |
2139 | +- spec = None |
2140 | +- elif spec_set is True: |
2141 | +- spec_set = original |
2142 | +- |
2143 | +- if spec is not None or spec_set is not None: |
2144 | +- if original is DEFAULT: |
2145 | +- raise TypeError("Can't use 'spec' with create=True") |
2146 | +- if isinstance(original, ClassTypes): |
2147 | +- # If we're patching out a class and there is a spec |
2148 | +- inherit = True |
2149 | +- |
2150 | +- Klass = MagicMock |
2151 | +- _kwargs = {} |
2152 | +- if new_callable is not None: |
2153 | +- Klass = new_callable |
2154 | +- elif spec is not None or spec_set is not None: |
2155 | +- this_spec = spec |
2156 | +- if spec_set is not None: |
2157 | +- this_spec = spec_set |
2158 | +- if _is_list(this_spec): |
2159 | +- not_callable = '__call__' not in this_spec |
2160 | +- else: |
2161 | +- not_callable = not _callable(this_spec) |
2162 | +- if not_callable: |
2163 | +- Klass = NonCallableMagicMock |
2164 | +- |
2165 | +- if spec is not None: |
2166 | +- _kwargs['spec'] = spec |
2167 | +- if spec_set is not None: |
2168 | +- _kwargs['spec_set'] = spec_set |
2169 | +- |
2170 | +- # add a name to mocks |
2171 | +- if (isinstance(Klass, type) and |
2172 | +- issubclass(Klass, NonCallableMock) and self.attribute): |
2173 | +- _kwargs['name'] = self.attribute |
2174 | +- |
2175 | +- _kwargs.update(kwargs) |
2176 | +- new = Klass(**_kwargs) |
2177 | +- |
2178 | +- if inherit and _is_instance_mock(new): |
2179 | +- # we can only tell if the instance should be callable if the |
2180 | +- # spec is not a list |
2181 | +- this_spec = spec |
2182 | +- if spec_set is not None: |
2183 | +- this_spec = spec_set |
2184 | +- if (not _is_list(this_spec) and not |
2185 | +- _instance_callable(this_spec)): |
2186 | +- Klass = NonCallableMagicMock |
2187 | +- |
2188 | +- _kwargs.pop('name') |
2189 | +- new.return_value = Klass(_new_parent=new, _new_name='()', |
2190 | +- **_kwargs) |
2191 | +- elif autospec is not None: |
2192 | +- # spec is ignored, new *must* be default, spec_set is treated |
2193 | +- # as a boolean. Should we check spec is not None and that spec_set |
2194 | +- # is a bool? |
2195 | +- if new is not DEFAULT: |
2196 | +- raise TypeError( |
2197 | +- "autospec creates the mock for you. Can't specify " |
2198 | +- "autospec and new." |
2199 | +- ) |
2200 | +- if original is DEFAULT: |
2201 | +- raise TypeError("Can't use 'autospec' with create=True") |
2202 | +- spec_set = bool(spec_set) |
2203 | +- if autospec is True: |
2204 | +- autospec = original |
2205 | +- |
2206 | +- new = create_autospec(autospec, spec_set=spec_set, |
2207 | +- _name=self.attribute, **kwargs) |
2208 | +- elif kwargs: |
2209 | +- # can't set keyword args when we aren't creating the mock |
2210 | +- # XXXX If new is a Mock we could call new.configure_mock(**kwargs) |
2211 | +- raise TypeError("Can't pass kwargs to a mock we aren't creating") |
2212 | +- |
2213 | +- new_attr = new |
2214 | +- |
2215 | +- self.temp_original = original |
2216 | +- self.is_local = local |
2217 | +- setattr(self.target, self.attribute, new_attr) |
2218 | +- if self.attribute_name is not None: |
2219 | +- extra_args = {} |
2220 | +- if self.new is DEFAULT: |
2221 | +- extra_args[self.attribute_name] = new |
2222 | +- for patching in self.additional_patchers: |
2223 | +- arg = patching.__enter__() |
2224 | +- if patching.new is DEFAULT: |
2225 | +- extra_args.update(arg) |
2226 | +- return extra_args |
2227 | +- |
2228 | +- return new |
2229 | +- |
2230 | +- |
2231 | +- def __exit__(self, *exc_info): |
2232 | +- """Undo the patch.""" |
2233 | +- if not _is_started(self): |
2234 | +- raise RuntimeError('stop called on unstarted patcher') |
2235 | +- |
2236 | +- if self.is_local and self.temp_original is not DEFAULT: |
2237 | +- setattr(self.target, self.attribute, self.temp_original) |
2238 | +- else: |
2239 | +- delattr(self.target, self.attribute) |
2240 | +- if not self.create and not hasattr(self.target, self.attribute): |
2241 | +- # needed for proxy objects like django settings |
2242 | +- setattr(self.target, self.attribute, self.temp_original) |
2243 | +- |
2244 | +- del self.temp_original |
2245 | +- del self.is_local |
2246 | +- del self.target |
2247 | +- for patcher in reversed(self.additional_patchers): |
2248 | +- if _is_started(patcher): |
2249 | +- patcher.__exit__(*exc_info) |
2250 | +- |
2251 | +- |
2252 | +- def start(self): |
2253 | +- """Activate a patch, returning any created mock.""" |
2254 | +- result = self.__enter__() |
2255 | +- self._active_patches.add(self) |
2256 | +- return result |
2257 | +- |
2258 | +- |
2259 | +- def stop(self): |
2260 | +- """Stop an active patch.""" |
2261 | +- self._active_patches.discard(self) |
2262 | +- return self.__exit__() |
2263 | +- |
2264 | +- |
2265 | +- |
2266 | +-def _get_target(target): |
2267 | +- try: |
2268 | +- target, attribute = target.rsplit('.', 1) |
2269 | +- except (TypeError, ValueError): |
2270 | +- raise TypeError("Need a valid target to patch. You supplied: %r" % |
2271 | +- (target,)) |
2272 | +- getter = lambda: _importer(target) |
2273 | +- return getter, attribute |
2274 | +- |
2275 | +- |
2276 | +-def _patch_object( |
2277 | +- target, attribute, new=DEFAULT, spec=None, |
2278 | +- create=False, spec_set=None, autospec=None, |
2279 | +- new_callable=None, **kwargs |
2280 | +- ): |
2281 | +- """ |
2282 | +- patch.object(target, attribute, new=DEFAULT, spec=None, create=False, |
2283 | +- spec_set=None, autospec=None, new_callable=None, **kwargs) |
2284 | +- |
2285 | +- patch the named member (`attribute`) on an object (`target`) with a mock |
2286 | +- object. |
2287 | +- |
2288 | +- `patch.object` can be used as a decorator, class decorator or a context |
2289 | +- manager. Arguments `new`, `spec`, `create`, `spec_set`, |
2290 | +- `autospec` and `new_callable` have the same meaning as for `patch`. Like |
2291 | +- `patch`, `patch.object` takes arbitrary keyword arguments for configuring |
2292 | +- the mock object it creates. |
2293 | +- |
2294 | +- When used as a class decorator `patch.object` honours `patch.TEST_PREFIX` |
2295 | +- for choosing which methods to wrap. |
2296 | +- """ |
2297 | +- getter = lambda: target |
2298 | +- return _patch( |
2299 | +- getter, attribute, new, spec, create, |
2300 | +- spec_set, autospec, new_callable, kwargs |
2301 | +- ) |
2302 | +- |
2303 | +- |
2304 | +-def _patch_multiple(target, spec=None, create=False, spec_set=None, |
2305 | +- autospec=None, new_callable=None, **kwargs): |
2306 | +- """Perform multiple patches in a single call. It takes the object to be |
2307 | +- patched (either as an object or a string to fetch the object by importing) |
2308 | +- and keyword arguments for the patches:: |
2309 | +- |
2310 | +- with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): |
2311 | +- ... |
2312 | +- |
2313 | +- Use `DEFAULT` as the value if you want `patch.multiple` to create |
2314 | +- mocks for you. In this case the created mocks are passed into a decorated |
2315 | +- function by keyword, and a dictionary is returned when `patch.multiple` is |
2316 | +- used as a context manager. |
2317 | +- |
2318 | +- `patch.multiple` can be used as a decorator, class decorator or a context |
2319 | +- manager. The arguments `spec`, `spec_set`, `create`, |
2320 | +- `autospec` and `new_callable` have the same meaning as for `patch`. These |
2321 | +- arguments will be applied to *all* patches done by `patch.multiple`. |
2322 | +- |
2323 | +- When used as a class decorator `patch.multiple` honours `patch.TEST_PREFIX` |
2324 | +- for choosing which methods to wrap. |
2325 | +- """ |
2326 | +- if type(target) in (unicode, str): |
2327 | +- getter = lambda: _importer(target) |
2328 | +- else: |
2329 | +- getter = lambda: target |
2330 | +- |
2331 | +- if not kwargs: |
2332 | +- raise ValueError( |
2333 | +- 'Must supply at least one keyword argument with patch.multiple' |
2334 | +- ) |
2335 | +- # need to wrap in a list for python 3, where items is a view |
2336 | +- items = list(kwargs.items()) |
2337 | +- attribute, new = items[0] |
2338 | +- patcher = _patch( |
2339 | +- getter, attribute, new, spec, create, spec_set, |
2340 | +- autospec, new_callable, {} |
2341 | +- ) |
2342 | +- patcher.attribute_name = attribute |
2343 | +- for attribute, new in items[1:]: |
2344 | +- this_patcher = _patch( |
2345 | +- getter, attribute, new, spec, create, spec_set, |
2346 | +- autospec, new_callable, {} |
2347 | +- ) |
2348 | +- this_patcher.attribute_name = attribute |
2349 | +- patcher.additional_patchers.append(this_patcher) |
2350 | +- return patcher |
2351 | +- |
2352 | +- |
2353 | +-def patch( |
2354 | +- target, new=DEFAULT, spec=None, create=False, |
2355 | +- spec_set=None, autospec=None, new_callable=None, **kwargs |
2356 | +- ): |
2357 | +- """ |
2358 | +- `patch` acts as a function decorator, class decorator or a context |
2359 | +- manager. Inside the body of the function or with statement, the `target` |
2360 | +- is patched with a `new` object. When the function/with statement exits |
2361 | +- the patch is undone. |
2362 | +- |
2363 | +- If `new` is omitted, then the target is replaced with a |
2364 | +- `MagicMock`. If `patch` is used as a decorator and `new` is |
2365 | +- omitted, the created mock is passed in as an extra argument to the |
2366 | +- decorated function. If `patch` is used as a context manager the created |
2367 | +- mock is returned by the context manager. |
2368 | +- |
2369 | +- `target` should be a string in the form `'package.module.ClassName'`. The |
2370 | +- `target` is imported and the specified object replaced with the `new` |
2371 | +- object, so the `target` must be importable from the environment you are |
2372 | +- calling `patch` from. The target is imported when the decorated function |
2373 | +- is executed, not at decoration time. |
2374 | +- |
2375 | +- The `spec` and `spec_set` keyword arguments are passed to the `MagicMock` |
2376 | +- if patch is creating one for you. |
2377 | +- |
2378 | +- In addition you can pass `spec=True` or `spec_set=True`, which causes |
2379 | +- patch to pass in the object being mocked as the spec/spec_set object. |
2380 | +- |
2381 | +- `new_callable` allows you to specify a different class, or callable object, |
2382 | +- that will be called to create the `new` object. By default `MagicMock` is |
2383 | +- used. |
2384 | +- |
2385 | +- A more powerful form of `spec` is `autospec`. If you set `autospec=True` |
2386 | +- then the mock with be created with a spec from the object being replaced. |
2387 | +- All attributes of the mock will also have the spec of the corresponding |
2388 | +- attribute of the object being replaced. Methods and functions being |
2389 | +- mocked will have their arguments checked and will raise a `TypeError` if |
2390 | +- they are called with the wrong signature. For mocks replacing a class, |
2391 | +- their return value (the 'instance') will have the same spec as the class. |
2392 | +- |
2393 | +- Instead of `autospec=True` you can pass `autospec=some_object` to use an |
2394 | +- arbitrary object as the spec instead of the one being replaced. |
2395 | +- |
2396 | +- By default `patch` will fail to replace attributes that don't exist. If |
2397 | +- you pass in `create=True`, and the attribute doesn't exist, patch will |
2398 | +- create the attribute for you when the patched function is called, and |
2399 | +- delete it again afterwards. This is useful for writing tests against |
2400 | +- attributes that your production code creates at runtime. It is off by by |
2401 | +- default because it can be dangerous. With it switched on you can write |
2402 | +- passing tests against APIs that don't actually exist! |
2403 | +- |
2404 | +- Patch can be used as a `TestCase` class decorator. It works by |
2405 | +- decorating each test method in the class. This reduces the boilerplate |
2406 | +- code when your test methods share a common patchings set. `patch` finds |
2407 | +- tests by looking for method names that start with `patch.TEST_PREFIX`. |
2408 | +- By default this is `test`, which matches the way `unittest` finds tests. |
2409 | +- You can specify an alternative prefix by setting `patch.TEST_PREFIX`. |
2410 | +- |
2411 | +- Patch can be used as a context manager, with the with statement. Here the |
2412 | +- patching applies to the indented block after the with statement. If you |
2413 | +- use "as" then the patched object will be bound to the name after the |
2414 | +- "as"; very useful if `patch` is creating a mock object for you. |
2415 | +- |
2416 | +- `patch` takes arbitrary keyword arguments. These will be passed to |
2417 | +- the `Mock` (or `new_callable`) on construction. |
2418 | +- |
2419 | +- `patch.dict(...)`, `patch.multiple(...)` and `patch.object(...)` are |
2420 | +- available for alternate use-cases. |
2421 | +- """ |
2422 | +- getter, attribute = _get_target(target) |
2423 | +- return _patch( |
2424 | +- getter, attribute, new, spec, create, |
2425 | +- spec_set, autospec, new_callable, kwargs |
2426 | +- ) |
2427 | +- |
2428 | +- |
2429 | +-class _patch_dict(object): |
2430 | +- """ |
2431 | +- Patch a dictionary, or dictionary like object, and restore the dictionary |
2432 | +- to its original state after the test. |
2433 | +- |
2434 | +- `in_dict` can be a dictionary or a mapping like container. If it is a |
2435 | +- mapping then it must at least support getting, setting and deleting items |
2436 | +- plus iterating over keys. |
2437 | +- |
2438 | +- `in_dict` can also be a string specifying the name of the dictionary, which |
2439 | +- will then be fetched by importing it. |
2440 | +- |
2441 | +- `values` can be a dictionary of values to set in the dictionary. `values` |
2442 | +- can also be an iterable of `(key, value)` pairs. |
2443 | +- |
2444 | +- If `clear` is True then the dictionary will be cleared before the new |
2445 | +- values are set. |
2446 | +- |
2447 | +- `patch.dict` can also be called with arbitrary keyword arguments to set |
2448 | +- values in the dictionary:: |
2449 | +- |
2450 | +- with patch.dict('sys.modules', mymodule=Mock(), other_module=Mock()): |
2451 | +- ... |
2452 | +- |
2453 | +- `patch.dict` can be used as a context manager, decorator or class |
2454 | +- decorator. When used as a class decorator `patch.dict` honours |
2455 | +- `patch.TEST_PREFIX` for choosing which methods to wrap. |
2456 | +- """ |
2457 | +- |
2458 | +- def __init__(self, in_dict, values=(), clear=False, **kwargs): |
2459 | +- if isinstance(in_dict, basestring): |
2460 | +- in_dict = _importer(in_dict) |
2461 | +- self.in_dict = in_dict |
2462 | +- # support any argument supported by dict(...) constructor |
2463 | +- self.values = dict(values) |
2464 | +- self.values.update(kwargs) |
2465 | +- self.clear = clear |
2466 | +- self._original = None |
2467 | +- |
2468 | +- |
2469 | +- def __call__(self, f): |
2470 | +- if isinstance(f, ClassTypes): |
2471 | +- return self.decorate_class(f) |
2472 | +- @wraps(f) |
2473 | +- def _inner(*args, **kw): |
2474 | +- self._patch_dict() |
2475 | +- try: |
2476 | +- return f(*args, **kw) |
2477 | +- finally: |
2478 | +- self._unpatch_dict() |
2479 | +- |
2480 | +- return _inner |
2481 | +- |
2482 | +- |
2483 | +- def decorate_class(self, klass): |
2484 | +- for attr in dir(klass): |
2485 | +- attr_value = getattr(klass, attr) |
2486 | +- if (attr.startswith(patch.TEST_PREFIX) and |
2487 | +- hasattr(attr_value, "__call__")): |
2488 | +- decorator = _patch_dict(self.in_dict, self.values, self.clear) |
2489 | +- decorated = decorator(attr_value) |
2490 | +- setattr(klass, attr, decorated) |
2491 | +- return klass |
2492 | +- |
2493 | +- |
2494 | +- def __enter__(self): |
2495 | +- """Patch the dict.""" |
2496 | +- self._patch_dict() |
2497 | +- |
2498 | +- |
2499 | +- def _patch_dict(self): |
2500 | +- values = self.values |
2501 | +- in_dict = self.in_dict |
2502 | +- clear = self.clear |
2503 | +- |
2504 | +- try: |
2505 | +- original = in_dict.copy() |
2506 | +- except AttributeError: |
2507 | +- # dict like object with no copy method |
2508 | +- # must support iteration over keys |
2509 | +- original = {} |
2510 | +- for key in in_dict: |
2511 | +- original[key] = in_dict[key] |
2512 | +- self._original = original |
2513 | +- |
2514 | +- if clear: |
2515 | +- _clear_dict(in_dict) |
2516 | +- |
2517 | +- try: |
2518 | +- in_dict.update(values) |
2519 | +- except AttributeError: |
2520 | +- # dict like object with no update method |
2521 | +- for key in values: |
2522 | +- in_dict[key] = values[key] |
2523 | +- |
2524 | +- |
2525 | +- def _unpatch_dict(self): |
2526 | +- in_dict = self.in_dict |
2527 | +- original = self._original |
2528 | +- |
2529 | +- _clear_dict(in_dict) |
2530 | +- |
2531 | +- try: |
2532 | +- in_dict.update(original) |
2533 | +- except AttributeError: |
2534 | +- for key in original: |
2535 | +- in_dict[key] = original[key] |
2536 | +- |
2537 | +- |
2538 | +- def __exit__(self, *args): |
2539 | +- """Unpatch the dict.""" |
2540 | +- self._unpatch_dict() |
2541 | +- return False |
2542 | +- |
2543 | +- start = __enter__ |
2544 | +- stop = __exit__ |
2545 | +- |
2546 | +- |
2547 | +-def _clear_dict(in_dict): |
2548 | +- try: |
2549 | +- in_dict.clear() |
2550 | +- except AttributeError: |
2551 | +- keys = list(in_dict) |
2552 | +- for key in keys: |
2553 | +- del in_dict[key] |
2554 | +- |
2555 | +- |
2556 | +-def _patch_stopall(): |
2557 | +- """Stop all active patches.""" |
2558 | +- for patch in list(_patch._active_patches): |
2559 | +- patch.stop() |
2560 | +- |
2561 | +- |
2562 | +-patch.object = _patch_object |
2563 | +-patch.dict = _patch_dict |
2564 | +-patch.multiple = _patch_multiple |
2565 | +-patch.stopall = _patch_stopall |
2566 | +-patch.TEST_PREFIX = 'test' |
2567 | +- |
2568 | +-magic_methods = ( |
2569 | +- "lt le gt ge eq ne " |
2570 | +- "getitem setitem delitem " |
2571 | +- "len contains iter " |
2572 | +- "hash str sizeof " |
2573 | +- "enter exit " |
2574 | +- "divmod neg pos abs invert " |
2575 | +- "complex int float index " |
2576 | +- "trunc floor ceil " |
2577 | +-) |
2578 | +- |
2579 | +-numerics = "add sub mul div floordiv mod lshift rshift and xor or pow " |
2580 | +-inplace = ' '.join('i%s' % n for n in numerics.split()) |
2581 | +-right = ' '.join('r%s' % n for n in numerics.split()) |
2582 | +-extra = '' |
2583 | +-if inPy3k: |
2584 | +- extra = 'bool next ' |
2585 | +-else: |
2586 | +- extra = 'unicode long nonzero oct hex truediv rtruediv ' |
2587 | +- |
2588 | +-# not including __prepare__, __instancecheck__, __subclasscheck__ |
2589 | +-# (as they are metaclass methods) |
2590 | +-# __del__ is not supported at all as it causes problems if it exists |
2591 | +- |
2592 | +-_non_defaults = set('__%s__' % method for method in [ |
2593 | +- 'cmp', 'getslice', 'setslice', 'coerce', 'subclasses', |
2594 | +- 'format', 'get', 'set', 'delete', 'reversed', |
2595 | +- 'missing', 'reduce', 'reduce_ex', 'getinitargs', |
2596 | +- 'getnewargs', 'getstate', 'setstate', 'getformat', |
2597 | +- 'setformat', 'repr', 'dir' |
2598 | +-]) |
2599 | +- |
2600 | +- |
2601 | +-def _get_method(name, func): |
2602 | +- "Turns a callable object (like a mock) into a real function" |
2603 | +- def method(self, *args, **kw): |
2604 | +- return func(self, *args, **kw) |
2605 | +- method.__name__ = name |
2606 | +- return method |
2607 | +- |
2608 | +- |
2609 | +-_magics = set( |
2610 | +- '__%s__' % method for method in |
2611 | +- ' '.join([magic_methods, numerics, inplace, right, extra]).split() |
2612 | +-) |
2613 | +- |
2614 | +-_all_magics = _magics | _non_defaults |
2615 | +- |
2616 | +-_unsupported_magics = set([ |
2617 | +- '__getattr__', '__setattr__', |
2618 | +- '__init__', '__new__', '__prepare__' |
2619 | +- '__instancecheck__', '__subclasscheck__', |
2620 | +- '__del__' |
2621 | +-]) |
2622 | +- |
2623 | +-_calculate_return_value = { |
2624 | +- '__hash__': lambda self: object.__hash__(self), |
2625 | +- '__str__': lambda self: object.__str__(self), |
2626 | +- '__sizeof__': lambda self: object.__sizeof__(self), |
2627 | +- '__unicode__': lambda self: unicode(object.__str__(self)), |
2628 | +-} |
2629 | +- |
2630 | +-_return_values = { |
2631 | +- '__lt__': NotImplemented, |
2632 | +- '__gt__': NotImplemented, |
2633 | +- '__le__': NotImplemented, |
2634 | +- '__ge__': NotImplemented, |
2635 | +- '__int__': 1, |
2636 | +- '__contains__': False, |
2637 | +- '__len__': 0, |
2638 | +- '__exit__': False, |
2639 | +- '__complex__': 1j, |
2640 | +- '__float__': 1.0, |
2641 | +- '__bool__': True, |
2642 | +- '__nonzero__': True, |
2643 | +- '__oct__': '1', |
2644 | +- '__hex__': '0x1', |
2645 | +- '__long__': long(1), |
2646 | +- '__index__': 1, |
2647 | +-} |
2648 | +- |
2649 | +- |
2650 | +-def _get_eq(self): |
2651 | +- def __eq__(other): |
2652 | +- ret_val = self.__eq__._mock_return_value |
2653 | +- if ret_val is not DEFAULT: |
2654 | +- return ret_val |
2655 | +- return self is other |
2656 | +- return __eq__ |
2657 | +- |
2658 | +-def _get_ne(self): |
2659 | +- def __ne__(other): |
2660 | +- if self.__ne__._mock_return_value is not DEFAULT: |
2661 | +- return DEFAULT |
2662 | +- return self is not other |
2663 | +- return __ne__ |
2664 | +- |
2665 | +-def _get_iter(self): |
2666 | +- def __iter__(): |
2667 | +- ret_val = self.__iter__._mock_return_value |
2668 | +- if ret_val is DEFAULT: |
2669 | +- return iter([]) |
2670 | +- # if ret_val was already an iterator, then calling iter on it should |
2671 | +- # return the iterator unchanged |
2672 | +- return iter(ret_val) |
2673 | +- return __iter__ |
2674 | +- |
2675 | +-_side_effect_methods = { |
2676 | +- '__eq__': _get_eq, |
2677 | +- '__ne__': _get_ne, |
2678 | +- '__iter__': _get_iter, |
2679 | +-} |
2680 | +- |
2681 | +- |
2682 | +- |
2683 | +-def _set_return_value(mock, method, name): |
2684 | +- fixed = _return_values.get(name, DEFAULT) |
2685 | +- if fixed is not DEFAULT: |
2686 | +- method.return_value = fixed |
2687 | +- return |
2688 | +- |
2689 | +- return_calulator = _calculate_return_value.get(name) |
2690 | +- if return_calulator is not None: |
2691 | +- try: |
2692 | +- return_value = return_calulator(mock) |
2693 | +- except AttributeError: |
2694 | +- # XXXX why do we return AttributeError here? |
2695 | +- # set it as a side_effect instead? |
2696 | +- return_value = AttributeError(name) |
2697 | +- method.return_value = return_value |
2698 | +- return |
2699 | +- |
2700 | +- side_effector = _side_effect_methods.get(name) |
2701 | +- if side_effector is not None: |
2702 | +- method.side_effect = side_effector(mock) |
2703 | +- |
2704 | +- |
2705 | +- |
2706 | +-class MagicMixin(object): |
2707 | +- def __init__(self, *args, **kw): |
2708 | +- _super(MagicMixin, self).__init__(*args, **kw) |
2709 | +- self._mock_set_magics() |
2710 | +- |
2711 | +- |
2712 | +- def _mock_set_magics(self): |
2713 | +- these_magics = _magics |
2714 | +- |
2715 | +- if self._mock_methods is not None: |
2716 | +- these_magics = _magics.intersection(self._mock_methods) |
2717 | +- |
2718 | +- remove_magics = set() |
2719 | +- remove_magics = _magics - these_magics |
2720 | +- |
2721 | +- for entry in remove_magics: |
2722 | +- if entry in type(self).__dict__: |
2723 | +- # remove unneeded magic methods |
2724 | +- delattr(self, entry) |
2725 | +- |
2726 | +- # don't overwrite existing attributes if called a second time |
2727 | +- these_magics = these_magics - set(type(self).__dict__) |
2728 | +- |
2729 | +- _type = type(self) |
2730 | +- for entry in these_magics: |
2731 | +- setattr(_type, entry, MagicProxy(entry, self)) |
2732 | +- |
2733 | +- |
2734 | +- |
2735 | +-class NonCallableMagicMock(MagicMixin, NonCallableMock): |
2736 | +- """A version of `MagicMock` that isn't callable.""" |
2737 | +- def mock_add_spec(self, spec, spec_set=False): |
2738 | +- """Add a spec to a mock. `spec` can either be an object or a |
2739 | +- list of strings. Only attributes on the `spec` can be fetched as |
2740 | +- attributes from the mock. |
2741 | +- |
2742 | +- If `spec_set` is True then only attributes on the spec can be set.""" |
2743 | +- self._mock_add_spec(spec, spec_set) |
2744 | +- self._mock_set_magics() |
2745 | +- |
2746 | +- |
2747 | +- |
2748 | +-class MagicMock(MagicMixin, Mock): |
2749 | +- """ |
2750 | +- MagicMock is a subclass of Mock with default implementations |
2751 | +- of most of the magic methods. You can use MagicMock without having to |
2752 | +- configure the magic methods yourself. |
2753 | +- |
2754 | +- If you use the `spec` or `spec_set` arguments then *only* magic |
2755 | +- methods that exist in the spec will be created. |
2756 | +- |
2757 | +- Attributes and the return value of a `MagicMock` will also be `MagicMocks`. |
2758 | +- """ |
2759 | +- def mock_add_spec(self, spec, spec_set=False): |
2760 | +- """Add a spec to a mock. `spec` can either be an object or a |
2761 | +- list of strings. Only attributes on the `spec` can be fetched as |
2762 | +- attributes from the mock. |
2763 | +- |
2764 | +- If `spec_set` is True then only attributes on the spec can be set.""" |
2765 | +- self._mock_add_spec(spec, spec_set) |
2766 | +- self._mock_set_magics() |
2767 | +- |
2768 | +- |
2769 | +- |
2770 | +-class MagicProxy(object): |
2771 | +- def __init__(self, name, parent): |
2772 | +- self.name = name |
2773 | +- self.parent = parent |
2774 | +- |
2775 | +- def __call__(self, *args, **kwargs): |
2776 | +- m = self.create_mock() |
2777 | +- return m(*args, **kwargs) |
2778 | +- |
2779 | +- def create_mock(self): |
2780 | +- entry = self.name |
2781 | +- parent = self.parent |
2782 | +- m = parent._get_child_mock(name=entry, _new_name=entry, |
2783 | +- _new_parent=parent) |
2784 | +- setattr(parent, entry, m) |
2785 | +- _set_return_value(parent, m, entry) |
2786 | +- return m |
2787 | +- |
2788 | +- def __get__(self, obj, _type=None): |
2789 | +- return self.create_mock() |
2790 | +- |
2791 | +- |
2792 | +- |
2793 | +-class _ANY(object): |
2794 | +- "A helper object that compares equal to everything." |
2795 | +- |
2796 | +- def __eq__(self, other): |
2797 | +- return True |
2798 | +- |
2799 | +- def __ne__(self, other): |
2800 | +- return False |
2801 | +- |
2802 | +- def __repr__(self): |
2803 | +- return '<ANY>' |
2804 | +- |
2805 | +-ANY = _ANY() |
2806 | +- |
2807 | +- |
2808 | +- |
2809 | +-def _format_call_signature(name, args, kwargs): |
2810 | +- message = '%s(%%s)' % name |
2811 | +- formatted_args = '' |
2812 | +- args_string = ', '.join([repr(arg) for arg in args]) |
2813 | +- kwargs_string = ', '.join([ |
2814 | +- '%s=%r' % (key, value) for key, value in kwargs.items() |
2815 | +- ]) |
2816 | +- if args_string: |
2817 | +- formatted_args = args_string |
2818 | +- if kwargs_string: |
2819 | +- if formatted_args: |
2820 | +- formatted_args += ', ' |
2821 | +- formatted_args += kwargs_string |
2822 | +- |
2823 | +- return message % formatted_args |
2824 | +- |
2825 | +- |
2826 | +- |
2827 | +-class _Call(tuple): |
2828 | +- """ |
2829 | +- A tuple for holding the results of a call to a mock, either in the form |
2830 | +- `(args, kwargs)` or `(name, args, kwargs)`. |
2831 | +- |
2832 | +- If args or kwargs are empty then a call tuple will compare equal to |
2833 | +- a tuple without those values. This makes comparisons less verbose:: |
2834 | +- |
2835 | +- _Call(('name', (), {})) == ('name',) |
2836 | +- _Call(('name', (1,), {})) == ('name', (1,)) |
2837 | +- _Call(((), {'a': 'b'})) == ({'a': 'b'},) |
2838 | +- |
2839 | +- The `_Call` object provides a useful shortcut for comparing with call:: |
2840 | +- |
2841 | +- _Call(((1, 2), {'a': 3})) == call(1, 2, a=3) |
2842 | +- _Call(('foo', (1, 2), {'a': 3})) == call.foo(1, 2, a=3) |
2843 | +- |
2844 | +- If the _Call has no name then it will match any name. |
2845 | +- """ |
2846 | +- def __new__(cls, value=(), name=None, parent=None, two=False, |
2847 | +- from_kall=True): |
2848 | +- name = '' |
2849 | +- args = () |
2850 | +- kwargs = {} |
2851 | +- _len = len(value) |
2852 | +- if _len == 3: |
2853 | +- name, args, kwargs = value |
2854 | +- elif _len == 2: |
2855 | +- first, second = value |
2856 | +- if isinstance(first, basestring): |
2857 | +- name = first |
2858 | +- if isinstance(second, tuple): |
2859 | +- args = second |
2860 | +- else: |
2861 | +- kwargs = second |
2862 | +- else: |
2863 | +- args, kwargs = first, second |
2864 | +- elif _len == 1: |
2865 | +- value, = value |
2866 | +- if isinstance(value, basestring): |
2867 | +- name = value |
2868 | +- elif isinstance(value, tuple): |
2869 | +- args = value |
2870 | +- else: |
2871 | +- kwargs = value |
2872 | +- |
2873 | +- if two: |
2874 | +- return tuple.__new__(cls, (args, kwargs)) |
2875 | +- |
2876 | +- return tuple.__new__(cls, (name, args, kwargs)) |
2877 | +- |
2878 | +- |
2879 | +- def __init__(self, value=(), name=None, parent=None, two=False, |
2880 | +- from_kall=True): |
2881 | +- self.name = name |
2882 | +- self.parent = parent |
2883 | +- self.from_kall = from_kall |
2884 | +- |
2885 | +- |
2886 | +- def __eq__(self, other): |
2887 | +- if other is ANY: |
2888 | +- return True |
2889 | +- try: |
2890 | +- len_other = len(other) |
2891 | +- except TypeError: |
2892 | +- return False |
2893 | +- |
2894 | +- self_name = '' |
2895 | +- if len(self) == 2: |
2896 | +- self_args, self_kwargs = self |
2897 | +- else: |
2898 | +- self_name, self_args, self_kwargs = self |
2899 | +- |
2900 | +- other_name = '' |
2901 | +- if len_other == 0: |
2902 | +- other_args, other_kwargs = (), {} |
2903 | +- elif len_other == 3: |
2904 | +- other_name, other_args, other_kwargs = other |
2905 | +- elif len_other == 1: |
2906 | +- value, = other |
2907 | +- if isinstance(value, tuple): |
2908 | +- other_args = value |
2909 | +- other_kwargs = {} |
2910 | +- elif isinstance(value, basestring): |
2911 | +- other_name = value |
2912 | +- other_args, other_kwargs = (), {} |
2913 | +- else: |
2914 | +- other_args = () |
2915 | +- other_kwargs = value |
2916 | +- else: |
2917 | +- # len 2 |
2918 | +- # could be (name, args) or (name, kwargs) or (args, kwargs) |
2919 | +- first, second = other |
2920 | +- if isinstance(first, basestring): |
2921 | +- other_name = first |
2922 | +- if isinstance(second, tuple): |
2923 | +- other_args, other_kwargs = second, {} |
2924 | +- else: |
2925 | +- other_args, other_kwargs = (), second |
2926 | +- else: |
2927 | +- other_args, other_kwargs = first, second |
2928 | +- |
2929 | +- if self_name and other_name != self_name: |
2930 | +- return False |
2931 | +- |
2932 | +- # this order is important for ANY to work! |
2933 | +- return (other_args, other_kwargs) == (self_args, self_kwargs) |
2934 | +- |
2935 | +- |
2936 | +- def __ne__(self, other): |
2937 | +- return not self.__eq__(other) |
2938 | +- |
2939 | +- |
2940 | +- def __call__(self, *args, **kwargs): |
2941 | +- if self.name is None: |
2942 | +- return _Call(('', args, kwargs), name='()') |
2943 | +- |
2944 | +- name = self.name + '()' |
2945 | +- return _Call((self.name, args, kwargs), name=name, parent=self) |
2946 | +- |
2947 | +- |
2948 | +- def __getattr__(self, attr): |
2949 | +- if self.name is None: |
2950 | +- return _Call(name=attr, from_kall=False) |
2951 | +- name = '%s.%s' % (self.name, attr) |
2952 | +- return _Call(name=name, parent=self, from_kall=False) |
2953 | +- |
2954 | +- |
2955 | +- def __repr__(self): |
2956 | +- if not self.from_kall: |
2957 | +- name = self.name or 'call' |
2958 | +- if name.startswith('()'): |
2959 | +- name = 'call%s' % name |
2960 | +- return name |
2961 | +- |
2962 | +- if len(self) == 2: |
2963 | +- name = 'call' |
2964 | +- args, kwargs = self |
2965 | +- else: |
2966 | +- name, args, kwargs = self |
2967 | +- if not name: |
2968 | +- name = 'call' |
2969 | +- elif not name.startswith('()'): |
2970 | +- name = 'call.%s' % name |
2971 | +- else: |
2972 | +- name = 'call%s' % name |
2973 | +- return _format_call_signature(name, args, kwargs) |
2974 | +- |
2975 | +- |
2976 | +- def call_list(self): |
2977 | +- """For a call object that represents multiple calls, `call_list` |
2978 | +- returns a list of all the intermediate calls as well as the |
2979 | +- final call.""" |
2980 | +- vals = [] |
2981 | +- thing = self |
2982 | +- while thing is not None: |
2983 | +- if thing.from_kall: |
2984 | +- vals.append(thing) |
2985 | +- thing = thing.parent |
2986 | +- return _CallList(reversed(vals)) |
2987 | +- |
2988 | +- |
2989 | +-call = _Call(from_kall=False) |
2990 | +- |
2991 | +- |
2992 | +- |
2993 | +-def create_autospec(spec, spec_set=False, instance=False, _parent=None, |
2994 | +- _name=None, **kwargs): |
2995 | +- """Create a mock object using another object as a spec. Attributes on the |
2996 | +- mock will use the corresponding attribute on the `spec` object as their |
2997 | +- spec. |
2998 | +- |
2999 | +- Functions or methods being mocked will have their arguments checked |
3000 | +- to check that they are called with the correct signature. |
3001 | +- |
3002 | +- If `spec_set` is True then attempting to set attributes that don't exist |
3003 | +- on the spec object will raise an `AttributeError`. |
3004 | +- |
3005 | +- If a class is used as a spec then the return value of the mock (the |
3006 | +- instance of the class) will have the same spec. You can use a class as the |
3007 | +- spec for an instance object by passing `instance=True`. The returned mock |
3008 | +- will only be callable if instances of the mock are callable. |
3009 | +- |
3010 | +- `create_autospec` also takes arbitrary keyword arguments that are passed to |
3011 | +- the constructor of the created mock.""" |
3012 | +- if _is_list(spec): |
3013 | +- # can't pass a list instance to the mock constructor as it will be |
3014 | +- # interpreted as a list of strings |
3015 | +- spec = type(spec) |
3016 | +- |
3017 | +- is_type = isinstance(spec, ClassTypes) |
3018 | +- |
3019 | +- _kwargs = {'spec': spec} |
3020 | +- if spec_set: |
3021 | +- _kwargs = {'spec_set': spec} |
3022 | +- elif spec is None: |
3023 | +- # None we mock with a normal mock without a spec |
3024 | +- _kwargs = {} |
3025 | +- |
3026 | +- _kwargs.update(kwargs) |
3027 | +- |
3028 | +- Klass = MagicMock |
3029 | +- if type(spec) in DescriptorTypes: |
3030 | +- # descriptors don't have a spec |
3031 | +- # because we don't know what type they return |
3032 | +- _kwargs = {} |
3033 | +- elif not _callable(spec): |
3034 | +- Klass = NonCallableMagicMock |
3035 | +- elif is_type and instance and not _instance_callable(spec): |
3036 | +- Klass = NonCallableMagicMock |
3037 | +- |
3038 | +- _new_name = _name |
3039 | +- if _parent is None: |
3040 | +- # for a top level object no _new_name should be set |
3041 | +- _new_name = '' |
3042 | +- |
3043 | +- mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name, |
3044 | +- name=_name, **_kwargs) |
3045 | +- |
3046 | +- if isinstance(spec, FunctionTypes): |
3047 | +- # should only happen at the top level because we don't |
3048 | +- # recurse for functions |
3049 | +- mock = _set_signature(mock, spec) |
3050 | +- else: |
3051 | +- _check_signature(spec, mock, is_type, instance) |
3052 | +- |
3053 | +- if _parent is not None and not instance: |
3054 | +- _parent._mock_children[_name] = mock |
3055 | +- |
3056 | +- if is_type and not instance and 'return_value' not in kwargs: |
3057 | +- mock.return_value = create_autospec(spec, spec_set, instance=True, |
3058 | +- _name='()', _parent=mock) |
3059 | +- |
3060 | +- for entry in dir(spec): |
3061 | +- if _is_magic(entry): |
3062 | +- # MagicMock already does the useful magic methods for us |
3063 | +- continue |
3064 | +- |
3065 | +- if isinstance(spec, FunctionTypes) and entry in FunctionAttributes: |
3066 | +- # allow a mock to actually be a function |
3067 | +- continue |
3068 | +- |
3069 | +- # XXXX do we need a better way of getting attributes without |
3070 | +- # triggering code execution (?) Probably not - we need the actual |
3071 | +- # object to mock it so we would rather trigger a property than mock |
3072 | +- # the property descriptor. Likewise we want to mock out dynamically |
3073 | +- # provided attributes. |
3074 | +- # XXXX what about attributes that raise exceptions other than |
3075 | +- # AttributeError on being fetched? |
3076 | +- # we could be resilient against it, or catch and propagate the |
3077 | +- # exception when the attribute is fetched from the mock |
3078 | +- try: |
3079 | +- original = getattr(spec, entry) |
3080 | +- except AttributeError: |
3081 | +- continue |
3082 | +- |
3083 | +- kwargs = {'spec': original} |
3084 | +- if spec_set: |
3085 | +- kwargs = {'spec_set': original} |
3086 | +- |
3087 | +- if not isinstance(original, FunctionTypes): |
3088 | +- new = _SpecState(original, spec_set, mock, entry, instance) |
3089 | +- mock._mock_children[entry] = new |
3090 | +- else: |
3091 | +- parent = mock |
3092 | +- if isinstance(spec, FunctionTypes): |
3093 | +- parent = mock.mock |
3094 | +- |
3095 | +- new = MagicMock(parent=parent, name=entry, _new_name=entry, |
3096 | +- _new_parent=parent, **kwargs) |
3097 | +- mock._mock_children[entry] = new |
3098 | +- skipfirst = _must_skip(spec, entry, is_type) |
3099 | +- _check_signature(original, new, skipfirst=skipfirst) |
3100 | +- |
3101 | +- # so functions created with _set_signature become instance attributes, |
3102 | +- # *plus* their underlying mock exists in _mock_children of the parent |
3103 | +- # mock. Adding to _mock_children may be unnecessary where we are also |
3104 | +- # setting as an instance attribute? |
3105 | +- if isinstance(new, FunctionTypes): |
3106 | +- setattr(mock, entry, new) |
3107 | +- |
3108 | +- return mock |
3109 | +- |
3110 | +- |
3111 | +-def _must_skip(spec, entry, is_type): |
3112 | +- if not isinstance(spec, ClassTypes): |
3113 | +- if entry in getattr(spec, '__dict__', {}): |
3114 | +- # instance attribute - shouldn't skip |
3115 | +- return False |
3116 | +- spec = spec.__class__ |
3117 | +- if not hasattr(spec, '__mro__'): |
3118 | +- # old style class: can't have descriptors anyway |
3119 | +- return is_type |
3120 | +- |
3121 | +- for klass in spec.__mro__: |
3122 | +- result = klass.__dict__.get(entry, DEFAULT) |
3123 | +- if result is DEFAULT: |
3124 | +- continue |
3125 | +- if isinstance(result, (staticmethod, classmethod)): |
3126 | +- return False |
3127 | +- return is_type |
3128 | +- |
3129 | +- # shouldn't get here unless function is a dynamically provided attribute |
3130 | +- # XXXX untested behaviour |
3131 | +- return is_type |
3132 | +- |
3133 | +- |
3134 | +-def _get_class(obj): |
3135 | +- try: |
3136 | +- return obj.__class__ |
3137 | +- except AttributeError: |
3138 | +- # in Python 2, _sre.SRE_Pattern objects have no __class__ |
3139 | +- return type(obj) |
3140 | +- |
3141 | +- |
3142 | +-class _SpecState(object): |
3143 | +- |
3144 | +- def __init__(self, spec, spec_set=False, parent=None, |
3145 | +- name=None, ids=None, instance=False): |
3146 | +- self.spec = spec |
3147 | +- self.ids = ids |
3148 | +- self.spec_set = spec_set |
3149 | +- self.parent = parent |
3150 | +- self.instance = instance |
3151 | +- self.name = name |
3152 | +- |
3153 | +- |
3154 | +-FunctionTypes = ( |
3155 | +- # python function |
3156 | +- type(create_autospec), |
3157 | +- # instance method |
3158 | +- type(ANY.__eq__), |
3159 | +- # unbound method |
3160 | +- type(_ANY.__eq__), |
3161 | +-) |
3162 | +- |
3163 | +-FunctionAttributes = set([ |
3164 | +- 'func_closure', |
3165 | +- 'func_code', |
3166 | +- 'func_defaults', |
3167 | +- 'func_dict', |
3168 | +- 'func_doc', |
3169 | +- 'func_globals', |
3170 | +- 'func_name', |
3171 | +-]) |
3172 | +- |
3173 | +- |
3174 | +-file_spec = None |
3175 | +- |
3176 | +- |
3177 | +-def mock_open(mock=None, read_data=''): |
3178 | +- """ |
3179 | +- A helper function to create a mock to replace the use of `open`. It works |
3180 | +- for `open` called directly or used as a context manager. |
3181 | +- |
3182 | +- The `mock` argument is the mock object to configure. If `None` (the |
3183 | +- default) then a `MagicMock` will be created for you, with the API limited |
3184 | +- to methods or attributes available on standard file handles. |
3185 | +- |
3186 | +- `read_data` is a string for the `read` method of the file handle to return. |
3187 | +- This is an empty string by default. |
3188 | +- """ |
3189 | +- global file_spec |
3190 | +- if file_spec is None: |
3191 | +- # set on first use |
3192 | +- if inPy3k: |
3193 | +- import _io |
3194 | +- file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) |
3195 | +- else: |
3196 | +- file_spec = file |
3197 | +- |
3198 | +- if mock is None: |
3199 | +- mock = MagicMock(name='open', spec=open) |
3200 | +- |
3201 | +- handle = MagicMock(spec=file_spec) |
3202 | +- handle.write.return_value = None |
3203 | +- handle.__enter__.return_value = handle |
3204 | +- handle.read.return_value = read_data |
3205 | +- |
3206 | +- mock.return_value = handle |
3207 | +- return mock |
3208 | +- |
3209 | +- |
3210 | +-class PropertyMock(Mock): |
3211 | +- """ |
3212 | +- A mock intended to be used as a property, or other descriptor, on a class. |
3213 | +- `PropertyMock` provides `__get__` and `__set__` methods so you can specify |
3214 | +- a return value when it is fetched. |
3215 | +- |
3216 | +- Fetching a `PropertyMock` instance from an object calls the mock, with |
3217 | +- no args. Setting it calls the mock with the value being set. |
3218 | +- """ |
3219 | +- def _get_child_mock(self, **kwargs): |
3220 | +- return MagicMock(**kwargs) |
3221 | +- |
3222 | +- def __get__(self, obj, obj_type): |
3223 | +- return self() |
3224 | +- def __set__(self, obj, val): |
3225 | +- self(val) |
3226 | ++# This file is part of Checkbox. |
3227 | ++# |
3228 | ++# Copyright 2013 Canonical Ltd. |
3229 | ++# Written by: |
3230 | ++# Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
3231 | ++# |
3232 | ++# Checkbox is free software: you can redistribute it and/or modify |
3233 | ++# it under the terms of the GNU General Public License as published by |
3234 | ++# the Free Software Foundation, either version 3 of the License, or |
3235 | ++# (at your option) any later version. |
3236 | ++# |
3237 | ++# Checkbox is distributed in the hope that it will be useful, |
3238 | ++# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3239 | ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3240 | ++# GNU General Public License for more details. |
3241 | ++# |
3242 | ++# You should have received a copy of the GNU General Public License |
3243 | ++# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
3244 | ++ |
3245 | ++""" |
3246 | ++:mod:`plainbox.vendor.mock` -- vendorized mock module |
3247 | ++===================================================== |
3248 | ++ |
3249 | ++This file has been patched-away by the Debian package as compared to the |
3250 | ++upstream version, not to ship a copy of a the 'mock' module that is now |
3251 | ++integrated into the upstream python3.3 release. |
3252 | ++""" |
3253 | ++ |
3254 | ++from unittest.mock import * |
3255 | diff --git a/debian/plainbox.manpages b/debian/plainbox.manpages |
3256 | new file mode 100644 |
3257 | index 0000000..5c9ed39 |
3258 | --- /dev/null |
3259 | +++ b/debian/plainbox.manpages |
3260 | @@ -0,0 +1,32 @@ |
3261 | +build/sphinx/man/CHECKBOX_DATA.7 |
3262 | +build/sphinx/man/CHECKBOX_SHARE.7 |
3263 | +build/sphinx/man/PLAINBOX_PROVIDER_DATA.7 |
3264 | +build/sphinx/man/PLAINBOX_SESSION_SHARE.7 |
3265 | +build/sphinx/man/manage.py.1 |
3266 | +build/sphinx/man/plainbox-category-units.7 |
3267 | +build/sphinx/man/plainbox-check-config.1 |
3268 | +build/sphinx/man/plainbox-dev-analyze.1 |
3269 | +build/sphinx/man/plainbox-dev-crash.1 |
3270 | +build/sphinx/man/plainbox-dev-list.1 |
3271 | +build/sphinx/man/plainbox-dev-logtest.1 |
3272 | +build/sphinx/man/plainbox-dev-parse.1 |
3273 | +build/sphinx/man/plainbox-dev-script.1 |
3274 | +build/sphinx/man/plainbox-dev-special.1 |
3275 | +build/sphinx/man/plainbox-dev.1 |
3276 | +build/sphinx/man/plainbox-device.1 |
3277 | +build/sphinx/man/plainbox-file-units.7 |
3278 | +build/sphinx/man/plainbox-job-units.7 |
3279 | +build/sphinx/man/plainbox-run.1 |
3280 | +build/sphinx/man/plainbox-self-test.1 |
3281 | +build/sphinx/man/plainbox-session-archive.1 |
3282 | +build/sphinx/man/plainbox-session-export.1 |
3283 | +build/sphinx/man/plainbox-session-list.1 |
3284 | +build/sphinx/man/plainbox-session-remove.1 |
3285 | +build/sphinx/man/plainbox-session-show.1 |
3286 | +build/sphinx/man/plainbox-session-structure.7 |
3287 | +build/sphinx/man/plainbox-session.1 |
3288 | +build/sphinx/man/plainbox-startprovider.1 |
3289 | +build/sphinx/man/plainbox-template-units.7 |
3290 | +build/sphinx/man/plainbox-test-plan-units.7 |
3291 | +build/sphinx/man/plainbox.1 |
3292 | +build/sphinx/man/plainbox.conf.5 |
3293 | diff --git a/debian/python3-plainbox-doc.doc-base b/debian/python3-plainbox-doc.doc-base |
3294 | new file mode 100644 |
3295 | index 0000000..2be3de8 |
3296 | --- /dev/null |
3297 | +++ b/debian/python3-plainbox-doc.doc-base |
3298 | @@ -0,0 +1,9 @@ |
3299 | +Document: plainbox |
3300 | +Title: Plainbox Manual |
3301 | +Author: Canonical Ltd. |
3302 | +Abstract: This manual describes what Plainbox is, and how it can be used. |
3303 | +Section: Programming/Python |
3304 | + |
3305 | +Format: HTML |
3306 | +Index: /usr/share/doc/python3-plainbox-doc/html/index.html |
3307 | +Files: /usr/share/doc/python3-plainbox-doc/html/*.html |
3308 | \ No newline at end of file |
3309 | diff --git a/debian/python3-plainbox-doc.docs b/debian/python3-plainbox-doc.docs |
3310 | new file mode 100644 |
3311 | index 0000000..6f7511e |
3312 | --- /dev/null |
3313 | +++ b/debian/python3-plainbox-doc.docs |
3314 | @@ -0,0 +1 @@ |
3315 | +build/sphinx/html |
3316 | diff --git a/debian/python3-plainbox.install b/debian/python3-plainbox.install |
3317 | new file mode 100644 |
3318 | index 0000000..a829b2d |
3319 | --- /dev/null |
3320 | +++ b/debian/python3-plainbox.install |
3321 | @@ -0,0 +1,2 @@ |
3322 | +build/mo/* usr/share/locale |
3323 | +plainbox/impl/providers/stubbox/build/mo/* usr/share/locale |
3324 | diff --git a/debian/python3-plainbox.links b/debian/python3-plainbox.links |
3325 | new file mode 100644 |
3326 | index 0000000..9297a04 |
3327 | --- /dev/null |
3328 | +++ b/debian/python3-plainbox.links |
3329 | @@ -0,0 +1,2 @@ |
3330 | +usr/share/python3-plainbox/data /usr/lib/python3/dist-packages/plainbox/data |
3331 | +usr/share/python3-plainbox/test-data /usr/lib/python3/dist-packages/plainbox/test-data |
3332 | diff --git a/debian/python3-plainbox.manpages b/debian/python3-plainbox.manpages |
3333 | new file mode 100644 |
3334 | index 0000000..a7372fb |
3335 | --- /dev/null |
3336 | +++ b/debian/python3-plainbox.manpages |
3337 | @@ -0,0 +1,2 @@ |
3338 | +build/sphinx/man/plainbox-trusted-launcher-1.1 |
3339 | +build/sphinx/man/plainbox-qml-shell.1 |
3340 | diff --git a/debian/python3-plainbox.preinst b/debian/python3-plainbox.preinst |
3341 | new file mode 100644 |
3342 | index 0000000..8e73be6 |
3343 | --- /dev/null |
3344 | +++ b/debian/python3-plainbox.preinst |
3345 | @@ -0,0 +1,44 @@ |
3346 | +#! /bin/sh |
3347 | +# Pre-install script for ‘python3-plainbox’. |
3348 | +# |
3349 | +# Manpage: ‘dh_installdeb(1)’ |
3350 | + |
3351 | +set -e |
3352 | + |
3353 | +# Summary of ways this script can be called: |
3354 | +# * <new-preinst> install |
3355 | +# * <new-preinst> install <old-version> |
3356 | +# * <new-preinst> upgrade <old-version> |
3357 | +# * <old-preinst> abort-upgrade <new-version> |
3358 | +# For details, see the Debian Policy §6.5 in the ‘debian-policy’ package |
3359 | +# or on the web at <URL:http://www.debian.org/doc/debian-policy/>. |
3360 | + |
3361 | +action="$1" |
3362 | + |
3363 | +case "$action" in |
3364 | + upgrade) |
3365 | + data_dir="/usr/lib/python3/dist-packages/plainbox/data" |
3366 | + if [ -d "$data_dir" ] && [ ! -L "$data_dir" ] ; then |
3367 | + # The ‘data’ location should be platform-independent. |
3368 | + # The new package will replace the directory with a symlink. |
3369 | + rm -rf "$data_dir" |
3370 | + fi |
3371 | + testdata_dir="/usr/lib/python3/dist-packages/plainbox/test-data" |
3372 | + if [ -d "$testdata_dir" ] && [ ! -L "$testdata_dir" ] ; then |
3373 | + # The ‘data’ location should be platform-independent. |
3374 | + # The new package will replace the directory with a symlink. |
3375 | + rm -rf "$testdata_dir" |
3376 | + fi |
3377 | + ;; |
3378 | + |
3379 | + install|abort-upgrade) |
3380 | + ;; |
3381 | + |
3382 | + *) |
3383 | + printf "preinst called with unknown action ‘%s’\n" "$action" >&2 |
3384 | + exit 1 |
3385 | + ;; |
3386 | + |
3387 | +esac |
3388 | + |
3389 | +#DEBHELPER# |
3390 | diff --git a/debian/rules b/debian/rules |
3391 | new file mode 100755 |
3392 | index 0000000..79ee81e |
3393 | --- /dev/null |
3394 | +++ b/debian/rules |
3395 | @@ -0,0 +1,49 @@ |
3396 | +#!/usr/bin/make -f |
3397 | +export PYBUILD_NAME=plainbox |
3398 | +export LANG= |
3399 | +export LANGUAGE= |
3400 | +export NO_PNG_PKG_MANGLE=1 |
3401 | + |
3402 | +%: |
3403 | + dh $@ --with=python3,sphinxdoc --buildsystem=pybuild |
3404 | + |
3405 | +override_dh_auto_build: |
3406 | + dh_auto_build --buildsystem=pybuild |
3407 | + # Build sphinx html documentation and man pages |
3408 | + # Use TEMP_HOME to work around https://bugs.launchpad.net/plainbox/+bug/1478906 |
3409 | + TEMP_HOME=`mktemp -d`; HOME=$$TEMP_HOME python3 setup.py build_sphinx -b html; rm -rf $$TEMP_HOME; |
3410 | + TEMP_HOME=`mktemp -d`; HOME=$$TEMP_HOME python3 setup.py build_sphinx -b man; rm -rf $$TEMP_HOME; |
3411 | + # Build plainbox translations |
3412 | + python3 setup.py build_i18n --merge-po |
3413 | + # Build translations for the bundled provider |
3414 | + PYTHONPATH=. ./plainbox/impl/providers/stubbox/manage.py i18n --dont-update-pot --dont-merge-po |
3415 | + PYTHONPATH=. ./plainbox/impl/providers/categories/manage.py i18n --dont-update-pot --dont-merge-po |
3416 | + PYTHONPATH=. ./plainbox/impl/providers/manifest/manage.py i18n --dont-update-pot --dont-merge-po |
3417 | + PYTHONPATH=. ./plainbox/impl/providers/exporters/manage.py i18n --dont-update-pot --dont-merge-po |
3418 | + |
3419 | +# Override dh_install to ensure that /usr/bin/plainbox is in the plainbox |
3420 | +# package and not in the python3-plainbox package. Also move the data and |
3421 | +# test-data directories to usr/share and provide symlinks (via |
3422 | +# python3-plainbox.links) for everything to work. |
3423 | +override_dh_install: |
3424 | + dh_install |
3425 | + mkdir -p debian/plainbox/usr/bin |
3426 | + mkdir -p debian/python3-plainbox/usr/share/python3-plainbox/ |
3427 | + mv debian/python3-plainbox/usr/bin/plainbox debian/plainbox/usr/bin/ |
3428 | + cp -R plainbox/data debian/python3-plainbox/usr/share/python3-plainbox/ |
3429 | + cp -R plainbox/test-data debian/python3-plainbox/usr/share/python3-plainbox/ |
3430 | + rm -rf $(foreach version,$(shell py3versions -s), debian/python3-plainbox/usr/lib/$(version)/dist-packages/plainbox/data) |
3431 | + rm -rf $(foreach version,$(shell py3versions -s), debian/python3-plainbox/usr/lib/$(version)/dist-packages/plainbox/test-data) |
3432 | + rm -f debian/python3-plainbox/usr/bin/stubbox |
3433 | + |
3434 | +# Override dh_clean to remove provider build artefacts |
3435 | +override_dh_clean: |
3436 | + dh_clean |
3437 | + rm -rf plainbox/impl/providers/stubbox/build |
3438 | + rm -rf plainbox/impl/providers/categories/build |
3439 | + |
3440 | +# Drop the empty python-3.4 directory |
3441 | +# Taken from https://wiki.debian.org/Python/LibraryStyleGuide |
3442 | +override_dh_python3: |
3443 | + dh_python3 |
3444 | + rm -rf debian/python3-plainbox/usr/lib/python3.? |
3445 | diff --git a/debian/source/format b/debian/source/format |
3446 | new file mode 100644 |
3447 | index 0000000..163aaf8 |
3448 | --- /dev/null |
3449 | +++ b/debian/source/format |
3450 | @@ -0,0 +1 @@ |
3451 | +3.0 (quilt) |
3452 | diff --git a/debian/source/options b/debian/source/options |
3453 | new file mode 100644 |
3454 | index 0000000..cccadcc |
3455 | --- /dev/null |
3456 | +++ b/debian/source/options |
3457 | @@ -0,0 +1 @@ |
3458 | +extend-diff-ignore = ".*\.po$" |
3459 | diff --git a/debian/tests/control b/debian/tests/control |
3460 | new file mode 100644 |
3461 | index 0000000..1079546 |
3462 | --- /dev/null |
3463 | +++ b/debian/tests/control |
3464 | @@ -0,0 +1,2 @@ |
3465 | +Tests: unit-tests |
3466 | +Depends: plainbox |
3467 | diff --git a/debian/tests/unit-tests b/debian/tests/unit-tests |
3468 | new file mode 100755 |
3469 | index 0000000..615deb5 |
3470 | --- /dev/null |
3471 | +++ b/debian/tests/unit-tests |
3472 | @@ -0,0 +1,10 @@ |
3473 | +#!/bin/sh |
3474 | +# autopkgtest check: run plainbox unit tests and ensure everything passes |
3475 | +# (C) 2013 Canonical Ltd. |
3476 | +# Author: Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
3477 | + |
3478 | +set -e |
3479 | + |
3480 | +cd $ADTTMP |
3481 | +plainbox self-test -u -v 2>&1 |
3482 | +echo "unit-tests: OK" |
3483 | diff --git a/debian/watch b/debian/watch |
3484 | new file mode 100644 |
3485 | index 0000000..bd41b5e |
3486 | --- /dev/null |
3487 | +++ b/debian/watch |
3488 | @@ -0,0 +1,3 @@ |
3489 | +version=3 |
3490 | +opts=uversionmangle=s/(rc|a|b|c)/~$1/ \ |
3491 | +http://pypi.debian.net/plainbox/plainbox-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz))) |
3492 | diff --git a/docs/author/index.rst b/docs/author/index.rst |
3493 | index b22bb46..6106b08 100644 |
3494 | --- a/docs/author/index.rst |
3495 | +++ b/docs/author/index.rst |
3496 | @@ -8,8 +8,15 @@ core. |
3497 | |
3498 | .. toctree:: |
3499 | intro.rst |
3500 | +<<<<<<< docs/author/index.rst |
3501 | qml-job-tutorial.rst |
3502 | providers.rst |
3503 | +======= |
3504 | + tutorial.rst |
3505 | + qml-job-tutorial.rst |
3506 | + providers.rst |
3507 | + whitelists.rst |
3508 | +>>>>>>> docs/author/index.rst |
3509 | rfc822.rst |
3510 | faq.rst |
3511 | |
3512 | diff --git a/docs/author/intro.rst b/docs/author/intro.rst |
3513 | index 2dae15d..93e9365 100644 |
3514 | --- a/docs/author/intro.rst |
3515 | +++ b/docs/author/intro.rst |
3516 | @@ -49,7 +49,11 @@ Terminology |
3517 | In developing or using Plainbox, you'll run into several unfamiliar terms. |
3518 | Check the :doc:`../glossary` to learn what they mean. In fact, you should |
3519 | probably check it now. Pay particular attention to the terms *Checkbox*, |
3520 | +<<<<<<< docs/author/intro.rst |
3521 | *Plainbox*, *job* and *provider*. |
3522 | +======= |
3523 | +*Plainbox*, *job*, *provider*, and *whitelist*. |
3524 | +>>>>>>> docs/author/intro.rst |
3525 | |
3526 | Getting Started |
3527 | --------------- |
3528 | @@ -73,7 +77,11 @@ tests. First up is a welcome screen: |
3529 | select tests. |
3530 | |
3531 | When you press the Enter key, ``checkbox-cli`` lets you select which |
3532 | +<<<<<<< docs/author/intro.rst |
3533 | test plan to use: |
3534 | +======= |
3535 | +whitelist to use: |
3536 | +>>>>>>> docs/author/intro.rst |
3537 | |
3538 | .. image:: cc2.png |
3539 | :height: 343 |
3540 | @@ -81,7 +89,11 @@ test plan to use: |
3541 | :scale: 100 |
3542 | :alt: checkbox-cli enables you to select which test suite to run. |
3543 | |
3544 | +<<<<<<< docs/author/intro.rst |
3545 | With a test plan selected, you can choose the individual tests to run: |
3546 | +======= |
3547 | +With a whitelist selected, you can choose the individual tests to run: |
3548 | +>>>>>>> docs/author/intro.rst |
3549 | |
3550 | .. image:: cc3.png |
3551 | :height: 600 |
3552 | @@ -117,7 +129,11 @@ A provider is described in a configuration file (stored in |
3553 | ``/usr/share/plainbox-providers-1``). This file describes where to find all |
3554 | the files from the provider. This file is usually managed automatically |
3555 | (more on this later). A provider can ship jobs, binaries, data and |
3556 | +<<<<<<< docs/author/intro.rst |
3557 | test plans. |
3558 | +======= |
3559 | +whitelists. |
3560 | +>>>>>>> docs/author/intro.rst |
3561 | |
3562 | A **job** or **test** is the smallest unit or description that Plainbox |
3563 | knows about. It describes a single test (historically they're called |
3564 | @@ -140,6 +156,13 @@ types include (but are not limited to): |
3565 | * ``shell`` -- An automated test that requires no user interaction; the |
3566 | test is passed or failed on the basis of the return value of the script |
3567 | or command. |
3568 | +<<<<<<< docs/author/intro.rst |
3569 | +======= |
3570 | + * ``local`` -- This type of job is similar to a ``shell`` test, but it |
3571 | + supports creating multiple tests from a single definition (say, to test |
3572 | + all the Ethernet ports on a computer). Jobs using the ``local`` plugin |
3573 | + are run when Plainbox is initialized. |
3574 | +>>>>>>> docs/author/intro.rst |
3575 | * ``user-interact`` -- A test that asks the user to perform some action |
3576 | *before* the test is performed. The test then passes or fails |
3577 | automatically based on the output of the test. An example is |
3578 | @@ -158,9 +181,176 @@ types include (but are not limited to): |
3579 | which probes the system to determine the maximum supported resolution |
3580 | and then asks the user to confirm that the resolution is correct. |
3581 | |
3582 | +<<<<<<< docs/author/intro.rst |
3583 | Each provider has a ``bin`` directory and all binaries there are available |
3584 | in the path. |
3585 | |
3586 | +======= |
3587 | +A fairly complex example definition is:: |
3588 | + |
3589 | + plugin: local |
3590 | + _summary: Automated test to walk multiple network cards and test each one in sequence. |
3591 | + id: ethernet/multi_nic |
3592 | + requires: |
3593 | + device.category == 'NETWORK' |
3594 | + _description: Automated test to walk multiple network cards and test each one in sequence. |
3595 | + command: |
3596 | + cat <<'EOF' | run_templates -s 'udev_resource | filter_templates -w "category=NETWORK" | awk "/path: / { print \$2 }" | xargs -n 1 sh -c "for i in \``ls /sys\$0/net 2>/dev/null\``; do echo \$0 \$i; done"' |
3597 | + plugin: shell |
3598 | + id: ethernet/multi_nic_$2 |
3599 | + requires: |
3600 | + package.name == 'ethtool' |
3601 | + package.name == 'nmap' |
3602 | + device.path == "$1" |
3603 | + user: root |
3604 | + environ: TEST_TARGET_FTP TEST_TARGET_IPERF TEST_USER TEST_PASS |
3605 | + command: network test -i $2 -t iperf --fail-threshold 80 |
3606 | + estimated_duration: 330.0 |
3607 | + description: |
3608 | + Testing for NIC $2 |
3609 | + EOF |
3610 | + |
3611 | +Key points to note include: |
3612 | + |
3613 | + * If a field name begins with an underscore, its value can be localized. |
3614 | + * The values of fields can appear on the same line as their field names, |
3615 | + as in ``plugin: local``; or they can appear on a subsequent line, which |
3616 | + is indented, as in the preceding example's ``requires: device.category |
3617 | + == 'NETWORK'``. |
3618 | + * The ``requires`` field can be used to specify dependencies; if the |
3619 | + specified condition is not met, the test does not run. |
3620 | + * The ``command`` field specifies the command that's used to run the test. |
3621 | + This can be a standard Linux command (or even a set of commands) or a |
3622 | + Checkbox test script. In this example's ``local`` test definition, the |
3623 | + first ``command`` line generates a list of network devices that is fed |
3624 | + to an embedded test, which is defined beginning with the second |
3625 | + ``plugin`` line immediately following the first ``command`` line. |
3626 | + * In this example, the line that reads ``EOF`` ends the |
3627 | + ``ethernet/ethtool_multi_nic_$2`` test's command; it's matched to the |
3628 | + ``EOF`` that's part of ``cat << 'EOF'`` near the start of that command. |
3629 | + |
3630 | +Each provider has a ``bin`` directory and all binaries there are available |
3631 | +in the path. |
3632 | + |
3633 | +Whitelists |
3634 | +`````````` |
3635 | + |
3636 | +In the job files we have a "universe" of known jobs. We don't normally want |
3637 | +to run them all; rather we want to select a subset depending on what we're |
3638 | +testing, and maybe give the user a way to fine-tune that selection. Also, |
3639 | +we need a way to determine the order in which they will run, beyond what |
3640 | +dependencies may provide. This is where the whitelist comes in; think of it |
3641 | +as a mask or selection filter from the universe of jobs. Whitelists support |
3642 | +regular expressions, and Plainbox will attempt to run tests in the order |
3643 | +shown in the whitelist. Again, providers ship whitelists in a specific |
3644 | +directory, and you can use ``plainbox`` to run a specific whitelist with |
3645 | +the ``-w`` option. |
3646 | + |
3647 | +You can also use ``plainbox`` to run a test with the ``-i`` syntax. This is |
3648 | +good for quickly running a job and ensuring it works well. |
3649 | + |
3650 | +Let's look at ``checkbox-cli`` for a moment. This is a "launcher"; it |
3651 | +specifies a set of configuration options for a specific testing purpose. |
3652 | +This enables us to create mini-clients for each testing purpose, without |
3653 | +changing the core utility (``checkbox-launcher``). For instance, let's look |
3654 | +at the launcher for ``canonical-certification-server``, which appears in |
3655 | +``./providers/plainbox-provider-certification-server/launcher/canonical-certification-server`` |
3656 | +in the Checkbox source tree:: |
3657 | + |
3658 | + #!/usr/bin/env checkbox-launcher |
3659 | + [welcome] |
3660 | + text = Welcome to System Certification! |
3661 | + This application will gather information from your system. Then you will be |
3662 | + asked manual tests to confirm that the system is working properly. Finally, |
3663 | + you will be asked for the Secure ID of the computer to submit the |
3664 | + information to the certification.canonical.com database. |
3665 | + To learn how to create or locate the Secure ID, please see here: |
3666 | + https://certification.canonical.com/ |
3667 | + |
3668 | + [suite] |
3669 | + # Whitelist(s) displayed in the suite selection screen |
3670 | + whitelist_filter = ^((network|storage|usb|virtualization)-only)|(server-(full|functional)-14.04)$ |
3671 | + # Whitelist(s) pre-selected in the suite selection screen, default whitelist(s) |
3672 | + whitelist_selection = ^server-full-14.04$ |
3673 | + |
3674 | + [transport] |
3675 | + submit_to = certification |
3676 | + |
3677 | + [config] |
3678 | + config_filename = canonical-certification.conf |
3679 | + |
3680 | +A launcher such as this sets up an environment that includes introductory |
3681 | +text to be shown to users, a filter to determine what whitelists to present |
3682 | +as options, information on where to (optionally) submit results, and a |
3683 | +configuration filename. This allows each provider to ship a launcher or |
3684 | +binary with which to launch its relevant tests. |
3685 | + |
3686 | +Developing Tests |
3687 | +```````````````` |
3688 | + |
3689 | +One way to deliver tests via Plainbox is to start your own provider. To |
3690 | +learn how to do that, see the :ref:`tutorial`. |
3691 | + |
3692 | +In other cases you want to add tests to the main Checkbox repository (which |
3693 | +is also what we recommend to keep tests centralized, unless they're so |
3694 | +purpose-specific that this makes no sense). |
3695 | + |
3696 | +This is a bit easier because the provider in question already exists. So |
3697 | +let's get started by branching a copy of ``lp:checkbox``. In brief, you |
3698 | +should change to your software development directory and type ``bzr branch |
3699 | +lp:checkbox my-branch`` to create a copy of the ``checkbox`` Launchpad |
3700 | +project in the ``my-branch`` subdirectory. You can then edit the files in |
3701 | +that subdirectory, upload the results to your own Launchpad account, and |
3702 | +request a merge. |
3703 | + |
3704 | +To begin, consider the files and subdirectories in the main Checkbox |
3705 | +development directory (``my-branch`` if you used the preceding ``bzr`` |
3706 | +command without change): |
3707 | + |
3708 | + * ``checkbox-gui`` -- Checkbox GUI components, used in desktop/laptop |
3709 | + testing |
3710 | + * ``checkbox-ng`` -- The Plainbox-based version of Checkbox |
3711 | + * ``checkbox-support`` -- Support code for many providers |
3712 | + * ``checkbox-touch`` -- A Checkbox frontend optimized for touch/tablet |
3713 | + devices |
3714 | + * ``mk-venv`` -- A symbolic link to a script used to set up an environment |
3715 | + for testing Checkbox |
3716 | + * ``plainbox`` -- A Python3 library and development tools at the heart of |
3717 | + Plainbox |
3718 | + * ``plainbox-client`` -- Unfinished Python3 interface for Checkbox |
3719 | + * ``providers`` -- Provider definitions, including test scripts |
3720 | + * ``README.md`` -- A file describing the contents of the subdirectory in |
3721 | + greater detail |
3722 | + * ``setup.py`` -- A setup script |
3723 | + * ``support`` -- Support code that's not released |
3724 | + * ``tarmac-verify`` -- A support script |
3725 | + * ``test-in-lxc.sh`` -- A support script for testing in an LXC |
3726 | + * ``test-in-vagrant.sh`` -- A support script for testing with Vagrant |
3727 | + * ``test-with-coverage`` -- A link to a support script for testing with |
3728 | + coverage |
3729 | + * ``Vagrantfile`` -- A Vagrant configuration file |
3730 | + |
3731 | +Let's say I want to write a test to ensure that the ubuntu user exists in |
3732 | +``/etc/passwd``. You need to remove any existing Checkbox provider |
3733 | +packages, lest they interfere with your new or modified tests. The |
3734 | +``setup.py`` script will set up a Plainbox development environment for you. |
3735 | + |
3736 | +We can write a simple job here, then add a requirement, perhaps a |
3737 | +dependency, then a script in the directory. Note that scripts can be |
3738 | +anything that's executable, we usually prefer either shell or Python but |
3739 | +anything goes. |
3740 | + |
3741 | +Plainbox will supply two environment variables, ``PLAINBOX_PROVIDER_DATA`` |
3742 | +and ``SHARE``, we usually try to use them in the job description only, not |
3743 | +in the scripts, to keep the scripts Plainbox-agnostic if possible. |
3744 | + |
3745 | +Once the test is running correctly, we can create a whitelist with a few |
3746 | +tests and name it. |
3747 | + |
3748 | +Once we get everything running correctly we can prepare and propose a merge |
3749 | +request using ``bzr`` as usual. |
3750 | + |
3751 | +>>>>>>> docs/author/intro.rst |
3752 | Other Questions |
3753 | --------------- |
3754 | |
3755 | diff --git a/docs/author/provider-files.rst b/docs/author/provider-files.rst |
3756 | index 34f21d4..d7ab8d1 100644 |
3757 | --- a/docs/author/provider-files.rst |
3758 | +++ b/docs/author/provider-files.rst |
3759 | @@ -53,6 +53,14 @@ jobs_dir |
3760 | Absolute pathname to a directory with :term:`job definitions <job>` |
3761 | as individual ``.txt`` files using the :doc:`job file format <jobs>`. |
3762 | |
3763 | +<<<<<<< docs/author/provider-files.rst |
3764 | +======= |
3765 | +whitelists_dir |
3766 | + Absolute pathname to a directory with :term:`whitelists <whitelist>` |
3767 | + as individual ``.whitelist`` files using the |
3768 | + :doc:`whitelist format <whitelists>`. |
3769 | + |
3770 | +>>>>>>> docs/author/provider-files.rst |
3771 | bin_dir |
3772 | Absolute pathname to a directory with additional executables required by |
3773 | any of the job definitions. |
3774 | @@ -75,6 +83,10 @@ location |
3775 | Variable Default Value |
3776 | ================ ===================== |
3777 | jobs_dir $location/jobs |
3778 | +<<<<<<< docs/author/provider-files.rst |
3779 | +======= |
3780 | + whitelists_dir $location/whitelists |
3781 | +>>>>>>> docs/author/provider-files.rst |
3782 | bin_dir $location/bin |
3783 | data_dir $location/data |
3784 | locale_dir $location/locale |
3785 | diff --git a/docs/author/provider-namespaces.rst b/docs/author/provider-namespaces.rst |
3786 | index e25036b..c5e73b7 100644 |
3787 | --- a/docs/author/provider-namespaces.rst |
3788 | +++ b/docs/author/provider-namespaces.rst |
3789 | @@ -120,7 +120,11 @@ The part of the provide name before the colon is used as the name-space. The |
3790 | colon is *not* a part of the name-space. |
3791 | |
3792 | The implicit name-space is used to construct non-partial job definition names |
3793 | +<<<<<<< docs/author/provider-namespaces.rst |
3794 | as well as to implicitly prefix each pattern inside test plans. |
3795 | +======= |
3796 | +as well as to implicitly prefix each pattern inside :term:`whitelists <whitelist>`. |
3797 | +>>>>>>> docs/author/provider-namespaces.rst |
3798 | |
3799 | Using Explicit Name-Spaces |
3800 | -------------------------- |
3801 | @@ -133,15 +137,25 @@ Explicit name-spaces need to be used in two situations: |
3802 | This is required as any partial ID may silently change the job it resolves |
3803 | to and we didn't want to introduce that ambiguity. |
3804 | |
3805 | +<<<<<<< docs/author/provider-namespaces.rst |
3806 | 2. When including a job from another name-space inside a test plan, e.g.:: |
3807 | |
3808 | ~/com.example.some:provider$ cat units/test-plan.pxu |
3809 | +======= |
3810 | +2. When including a job from another name-space inside a whitelist, e.g.:: |
3811 | + |
3812 | + ~/com.example.some:provider$ cat whitelists/cross.whitelist |
3813 | +>>>>>>> docs/author/provider-namespaces.rst |
3814 | job-a |
3815 | job-b |
3816 | com\.example\.other::job-a |
3817 | ~com.example.some:provider$ |
3818 | |
3819 | +<<<<<<< docs/author/provider-namespaces.rst |
3820 | Here the test plan names three jobs: |
3821 | +======= |
3822 | + Here the whitelist names three jobs: |
3823 | +>>>>>>> docs/author/provider-namespaces.rst |
3824 | |
3825 | * com.example.some::job-a |
3826 | * com.example.some::job-b |
3827 | diff --git a/docs/author/provider-template.rst b/docs/author/provider-template.rst |
3828 | index ae5bb76..302bf7d 100644 |
3829 | --- a/docs/author/provider-template.rst |
3830 | +++ b/docs/author/provider-template.rst |
3831 | @@ -23,14 +23,27 @@ The following files and directories are generated:: |
3832 | ├── data |
3833 | │ ├── example.dat |
3834 | │ └── README.md |
3835 | +<<<<<<< docs/author/provider-template.rst |
3836 | +======= |
3837 | + ├── jobs |
3838 | + │ ├── examples-intermediate.txt |
3839 | + │ ├── examples-normal.txt |
3840 | + │ └── examples-trivial.txt |
3841 | +>>>>>>> docs/author/provider-template.rst |
3842 | ├── manage.py |
3843 | ├── po |
3844 | │ └── POTFILES.in |
3845 | ├── README.md |
3846 | +<<<<<<< docs/author/provider-template.rst |
3847 | └── units |
3848 | ├── examples-intermediate.txt |
3849 | ├── examples-normal.txt |
3850 | └── examples-trivial.txt |
3851 | +======= |
3852 | + └── whitelists |
3853 | + ├── normal.whitelist |
3854 | + └── trivial.whitelist |
3855 | +>>>>>>> docs/author/provider-template.rst |
3856 | |
3857 | Generated Content |
3858 | ================= |
3859 | @@ -56,8 +69,15 @@ README.md |
3860 | Plainbox parlance, is the smallest piece of executable test code. Each |
3861 | job has a name and a number of other attributes. |
3862 | |
3863 | +<<<<<<< docs/author/provider-template.rst |
3864 | Jobs can be arranged in lists, test plans if you will. You can create as |
3865 | many test plans as you need, referring to arbitrary subsets of your jobs. |
3866 | +======= |
3867 | + Jobs can be arranged in lists, test plans if you will that are known |
3868 | + as "whitelists". Those are defined in the ``whitelists/`` directory, |
3869 | + this time one per file. You can create as many whitelists as you need, |
3870 | + referring to arbitrary subsets of your jobs. |
3871 | +>>>>>>> docs/author/provider-template.rst |
3872 | |
3873 | Then there are the ``bin/`` and ``data/`` directories. Those are |
3874 | entirely for custom content you may need. You can put arbitrary |
3875 | @@ -335,7 +355,11 @@ jobs/examples-intermediate.txt |
3876 | estimated_duration: 30 |
3877 | |
3878 | |
3879 | +<<<<<<< docs/author/provider-template.rst |
3880 | po/POTFILES.in |
3881 | +======= |
3882 | +po/PORFILES.in |
3883 | +>>>>>>> docs/author/provider-template.rst |
3884 | -------------- |
3885 | |
3886 | :: |
3887 | @@ -345,3 +369,23 @@ po/POTFILES.in |
3888 | [type: gettext/rfc822deb] jobs/examples-normal.txt |
3889 | [type: gettext/rfc822deb] jobs/examples-intermediate.txt |
3890 | manage.py |
3891 | +<<<<<<< docs/author/provider-template.rst |
3892 | +======= |
3893 | + |
3894 | +whitelists/trivial.whitelist |
3895 | +---------------------------- |
3896 | + |
3897 | +:: |
3898 | + |
3899 | + # select two trivial jobs by directly selecting their names |
3900 | + examples/trivial/always-pass |
3901 | + examples/trivial/always-fail |
3902 | + |
3903 | +whitelists/normal.whitelist |
3904 | +--------------------------- |
3905 | + |
3906 | +:: |
3907 | + |
3908 | + # use regular expression to select all normal jobs |
3909 | + examples/normal/.* |
3910 | +>>>>>>> docs/author/provider-template.rst |
3911 | diff --git a/docs/author/providers.rst b/docs/author/providers.rst |
3912 | index cc1bb6c..40d4dc5 100644 |
3913 | --- a/docs/author/providers.rst |
3914 | +++ b/docs/author/providers.rst |
3915 | @@ -5,7 +5,11 @@ Providers |
3916 | Providers are a new feature introduced in Plainbox 0.5. They allow third party |
3917 | developers to produce and maintain private and public test collections. |
3918 | |
3919 | +<<<<<<< docs/author/providers.rst |
3920 | All :term:`jobs <job>` and test plans are now loaded from a provider. This |
3921 | +======= |
3922 | +All :term:`jobs <job>` and :term:`whitelists <whitelist>` are now loaded from a provider. This |
3923 | +>>>>>>> docs/author/providers.rst |
3924 | also affects the :term:`Checkbox` project that now produces a custom user |
3925 | interface and a number of providers for various purposes. |
3926 | |
3927 | diff --git a/docs/author/tutorial.rst b/docs/author/tutorial.rst |
3928 | new file mode 100644 |
3929 | index 0000000..892cb19 |
3930 | --- /dev/null |
3931 | +++ b/docs/author/tutorial.rst |
3932 | @@ -0,0 +1,177 @@ |
3933 | +.. _tutorial: |
3934 | + |
3935 | +======== |
3936 | +Tutorial |
3937 | +======== |
3938 | + |
3939 | +To best illustrate how providers work, we will walk through creating one |
3940 | +step-by-step. At the end of this tutorial you will have a provider which adds |
3941 | +a new :term:`whitelist`, several new jobs and the scripts and test data |
3942 | +supporting those jobs. Before starting this tutorial you will need to have a |
3943 | +running version of :term:`Plainbox` installed. You can either install it from |
3944 | +the repositories of Debian or its derivatives by running ``apt-get install |
3945 | +plainbox``, or if you prefer to work with the source, see :doc:`Getting |
3946 | +started with development <../dev/intro>`. There is also a Launchpad PPA with |
3947 | +the very latest development build for Ubuntu, which is `ppa:checkbox-dev/ppa`. |
3948 | + |
3949 | +#. To get started we create an initial template for our provider by running |
3950 | + ``plainbox startprovider com.example:myprovider``. |
3951 | + |
3952 | +#. This will create a directory called ``com.example:myprovider``. |
3953 | + Change to this directory and you will see that it contains:: |
3954 | + |
3955 | + /bin |
3956 | + /data |
3957 | + /integration-tests |
3958 | + /jobs |
3959 | + manage.py |
3960 | + README.md |
3961 | + /whitelists |
3962 | + |
3963 | + The ``manage.py`` script is a helper script for developing the provider. |
3964 | + It provides a set of commands which assist in validating the correctness |
3965 | + of the provider and making it ready for distribution. |
3966 | + |
3967 | +#. Let’s create some jobs first by changing to the jobs directory. It currently |
3968 | + contains a file called category.txt which serves as an example of how |
3969 | + jobs should look. Let’s delete it and instead create a file called |
3970 | + ``myjobs.txt``. This can contain the following simple jobs:: |
3971 | + |
3972 | + plugin: shell |
3973 | + name: myjobs/shell_command |
3974 | + command: true |
3975 | + _description: |
3976 | + An example job that uses a command provided by the shell. |
3977 | + |
3978 | + plugin: shell |
3979 | + name: myjobs/provider_command |
3980 | + command: mycommand |
3981 | + _description: |
3982 | + An example job that uses a test command provided by this provider. |
3983 | + |
3984 | + At this point we can check that everything looks okay by running the command |
3985 | + ``./manage.py info`` which displays some information about the provider. The |
3986 | + output should be something like:: |
3987 | + |
3988 | + [Provider MetaData] |
3989 | + name: com.example:myprovider |
3990 | + version: 1.0 |
3991 | + [Job Definitions] |
3992 | + 'myjobs/builtin_command', from jobs/myjobs.txt:1-5 |
3993 | + 'myjobs/provider_command', from jobs/myjobs.txt:7-11 |
3994 | + [White Lists] |
3995 | + 'category', from whitelists/category.whitelist:1-1 |
3996 | + |
3997 | + This shows all three jobs from the job file we added - great! |
3998 | + |
3999 | +#. Next we need to change directory to ``bin`` to add the command used by the |
4000 | + job ``myjobs/this_provider_command``. We create a file there called |
4001 | + ``mycommand`` which contains the following text:: |
4002 | + |
4003 | + #!/bin/sh |
4004 | + test `cat $CHECKBOX_SHARE/data/testfile` = 'expected' |
4005 | + |
4006 | + This needs to be executable to be used in the job command so we need to run |
4007 | + ``chmod a+x mycommand`` to make it executable. |
4008 | + |
4009 | + You'll notice the command uses a file in ``$CHECKBOX_SHARE/data`` - we'll |
4010 | + add this file to our provider next. |
4011 | + |
4012 | +#. Because the command we’re using uses a file that we expect to be located in |
4013 | + ``$CHECKBOX_SHARE/data``, we need to add this file to our provider so that |
4014 | + after the provider is installed this file is available in that location. |
4015 | + First we need to change to the directory called ``data``, then as indicated |
4016 | + by the contents of the script we wrote in the previous step, we need to |
4017 | + create a file there called ``testfile`` with the contents:: |
4018 | + |
4019 | + expected |
4020 | + |
4021 | + As simple as that! |
4022 | + |
4023 | +#. Lastly we need to add a :term:`whitelist` that utilizes the jobs we created |
4024 | + earlier. We need to change to the directory called ``whitelists``. As with |
4025 | + the ``jobs`` directory there is already an example file there called |
4026 | + ``category.whitelist``. We can delete that and add a file called |
4027 | + ``mywhitelist.whitelist``. The contents should be:: |
4028 | + |
4029 | + myjobs/shell_command |
4030 | + myjobs/provider_command |
4031 | + |
4032 | + The ``miscellanea/submission_resources`` and ``graphics/glxgears`` jobs |
4033 | + are from the default provider that is part of Plainbox. |
4034 | + |
4035 | + We can check that everything is correct with the whitelist by running the |
4036 | + ``./manage.py info`` command again. The output should be like:: |
4037 | + |
4038 | + [Provider MetaData] |
4039 | + name: com.example:myprovider |
4040 | + version: 1.0 |
4041 | + [Job Definitions] |
4042 | + 'myjobs/builtin_command', from jobs/myjobs.txt:1-5 |
4043 | + 'myjobs/provider_command', from jobs/myjobs.txt:7-11 |
4044 | + [White Lists] |
4045 | + 'mywhitelist', from whitelists/mywhitelist.whitelist:1-2 |
4046 | + |
4047 | + Our new :term:`whitelist` is listed there. |
4048 | + |
4049 | +#. Now we have a provider we need to test it to make sure everything is |
4050 | + correct. The first thing to do is to install the provider so that it |
4051 | + it visible to Plainbox. Run ``./manage.py develop`` then run |
4052 | + ``plainbox dev list provider``. Your provider should be in the list |
4053 | + that is displayed. |
4054 | + |
4055 | +#. We should also make sure the whole provider works end-to-end by running |
4056 | + the :term:`whitelist` which it provides. Run the following command - |
4057 | + ``plainbox run -w whitelists/mywhitelist.whitelist``. |
4058 | + |
4059 | +#. Assuming everything works okay, we can now package the provider for |
4060 | + distribution. This involves creating a basic ``debian`` directory |
4061 | + containing all of the files needed for packaging your provider. Create |
4062 | + a directory called ``debian`` at the base of your provider, and then |
4063 | + create the following files within it. |
4064 | + |
4065 | + ``compat``:: |
4066 | + |
4067 | + 9 |
4068 | + |
4069 | + ``control``:: |
4070 | + |
4071 | + Source: plainbox-myprovider |
4072 | + Section: utils |
4073 | + Priority: optional |
4074 | + Maintainer: Brendan Donegan <brendan.donegan@canonical.com> |
4075 | + Standards-Version: 3.9.3 |
4076 | + X-Python3-Version: >= 3.2 |
4077 | + Build-Depends: debhelper (>= 9.2), |
4078 | + lsb-release, |
4079 | + python3 (>= 3.2), |
4080 | + python3-plainbox |
4081 | + |
4082 | + Package: plainbox-myprovider |
4083 | + Architecture: all |
4084 | + Depends: plainbox-provider-checkbox |
4085 | + Description: My whitelist provider |
4086 | + A provider for Plainbox. |
4087 | + |
4088 | + ``rules``:: |
4089 | + |
4090 | + #!/usr/bin/make -f |
4091 | + %: |
4092 | + dh "$@" |
4093 | + |
4094 | + override_dh_auto_build: |
4095 | + $(CURDIR)/manage.py install |
4096 | + |
4097 | + Note that the ``rules`` file must be executable. Make it so with |
4098 | + ``chmod a+x rules``. Also, be careful with the indentation in the |
4099 | + file - all indents must be actual TAB characters, not four spaces |
4100 | + for example. |
4101 | + |
4102 | + ``source/format``:: |
4103 | + |
4104 | + 3.0 (native) |
4105 | + |
4106 | + Finally we should create a ``changelog`` file. The easiest way to do this |
4107 | + is to run the command ``dch --create 'Initial release.'``. You'll need to |
4108 | + edit the field ``PACKAGE`` to the name of your provider and the field |
4109 | + ``VERSION`` to something like ``0.1``. |
4110 | diff --git a/docs/author/whitelists.rst b/docs/author/whitelists.rst |
4111 | new file mode 100644 |
4112 | index 0000000..7b5f162 |
4113 | --- /dev/null |
4114 | +++ b/docs/author/whitelists.rst |
4115 | @@ -0,0 +1,120 @@ |
4116 | +======================== |
4117 | +Checkbox Whitelist Files |
4118 | +======================== |
4119 | + |
4120 | +When creating a test suite for a particular purpose, it will be necessary to |
4121 | +specify which tests to run and which order they should run in. For this purpose |
4122 | +Checkbox provides the concept of Whitelists. |
4123 | + |
4124 | +Whitelist Format |
4125 | +================ |
4126 | + |
4127 | +A whitelist is a text file containing a line-seperated sequence of patterns, |
4128 | +each representing one or more 'jobs'. These patterns are in the Python regular |
4129 | +expression syntax. Comments may be included in the file by starting the line |
4130 | +with '#'. |
4131 | + |
4132 | +Minimal Whitelist File |
4133 | +====================== |
4134 | + |
4135 | +In order to be useful a whitelist file needs to include a particular subset of |
4136 | +jobs which provide Checkbox with all of the information it needs to run tests |
4137 | +properly. These include jobs which attach hardware information and resource |
4138 | +jobs which provide other jobs with information of the environment they a |
4139 | +re running in (available hardware, available packages etc). To make this easy |
4140 | +to do a single job exists whose purpose is to execute all of these other jobs:: |
4141 | + |
4142 | + miscellanea/submission-resources |
4143 | + |
4144 | +This should be included as the first job in any whitelist. |
4145 | + |
4146 | +Job Categories |
4147 | +============== |
4148 | + |
4149 | +In order to allow Checkbox to display jobs by category in the UI it is |
4150 | +necessary to include a particular local job which itself generates jobs which |
4151 | +belong to that category. This job will normally look like ``__<category>__`` |
4152 | +where <category> is the name of the job file which contains the job. This is |
4153 | +indicated again by the prefix of the job (before the ``/`` in the job name). |
4154 | +As a quick example, the job ``graphics/glxgears`` is contained in |
4155 | +``graphics.txt``. Therefore we should include the ``__graphics__`` job so that |
4156 | +the ``graphics/glxgears`` job shows correctly under the category. The |
4157 | +``__graphics__`` job itself looks like:: |
4158 | + |
4159 | + name: __graphics__ |
4160 | + plugin: local |
4161 | + _description: Graphics tests |
4162 | + command: |
4163 | + shopt -s extglob |
4164 | + cat $CHECKBOX_SHARE/jobs/graphics.txt?(.in) |
4165 | + |
4166 | +Checkbox will interpret this job as a request to display any job in |
4167 | +``graphics.txt`` (or its untranslated version ``graphics.txt.in``) under the |
4168 | +heading shown in the description of this job (in this case 'Graphics tests'). |
4169 | + |
4170 | +Tutorial |
4171 | +======== |
4172 | + |
4173 | +To compound what we discussed before, below is a brief tutorial which walks |
4174 | +through assembling a basic whitelist file. |
4175 | + |
4176 | +1. First we need to create a file, let's name it tutorial.whitelist. |
4177 | +Whitelists don't have to end with the .whitelist suffix but this is the |
4178 | +convention used to help identify them. |
4179 | +2. We start by adding the one job that is required for all whitelists, as |
4180 | +explained above in the section 'Minimal Whitelist File', so our whitelist file |
4181 | +looks like:: |
4182 | + |
4183 | + miscellanea/submission-resources |
4184 | + |
4185 | +3. Next we should choose some jobs that we want to run. This all depends on |
4186 | +your specific use-case of course, but I've selected a few jobs that will help |
4187 | +clearly illustrate more of the concepts involved in whitelists. These jobs will |
4188 | +give us a whitelist file that looks like:: |
4189 | + |
4190 | + miscellanea/submission-resources |
4191 | + cpu/clocktest |
4192 | + ethernet/multi_nic |
4193 | + ethernet/multi_nic_eth0 |
4194 | + graphics/glxgears |
4195 | + |
4196 | + If we run this whitelist now then all of these jobs will be executed and a |
4197 | + valid test submission will be created, but we can still improve it in a couple |
4198 | + of ways. |
4199 | + |
4200 | +4. The first way is by adding the necessary jobs to allow the Checkbox UI to |
4201 | +group the jobs into specific categories. To do this we need to add a job with |
4202 | +a name like ``__<category>__`` for each category. We have three categories in |
4203 | +our whitelist file - cpu, ethernet and graphics. The category of the job is |
4204 | +the prefix of the job name prior to the ``/``. So now our whitelist file looks |
4205 | +like:: |
4206 | + |
4207 | + miscellanea/submission-resources |
4208 | + __cpu__ |
4209 | + __ethernet__ |
4210 | + __graphics__ |
4211 | + cpu/clocktest |
4212 | + ethernet/multi_nic |
4213 | + ethernet/multi_nic_eth0 |
4214 | + graphics/glxgears |
4215 | + |
4216 | + Now the Checkbox UI will group the jobs into these categories. |
4217 | + |
4218 | +5. Although it's not immediately apparent there is another problem with this |
4219 | +whitelist. The ``ethernet/multi_nic`` tests are only able to include one job |
4220 | +for the ethernet port 'eth0'. It would be better if we included all of the |
4221 | +jobs generated by 'ethernet/multi_nic', no matter how many ethernet ports are |
4222 | +present on the system under test. The best way to do this is to write the |
4223 | +pattern so that it matches all of the possible job names. We can take advantage |
4224 | +of the Python regular expression syntax and use the ``\d`` special character |
4225 | +to match any decimal number. After doing this the whitelist file will look |
4226 | +like this:: |
4227 | + |
4228 | + miscellanea/submission-resources |
4229 | + __cpu__ |
4230 | + __ethernet__ |
4231 | + __graphics__ |
4232 | + cpu/clocktest |
4233 | + ethernet/multi_nic |
4234 | + ethernet/multi_nic_eth\d |
4235 | + graphics/glxgears |
4236 | diff --git a/docs/conf.py b/docs/conf.py |
4237 | index 07bd76e..4ff2437 100644 |
4238 | --- a/docs/conf.py |
4239 | +++ b/docs/conf.py |
4240 | @@ -124,11 +124,16 @@ pygments_style = 'sphinx' |
4241 | # Use our custom theme. For now it only adds Disqus.com support but we may |
4242 | # customize it further later on. The theme is called 'plainbox' and has one |
4243 | # option which controls if disqus is active or not. |
4244 | +<<<<<<< docs/conf.py |
4245 | html_theme = 'plainbox' |
4246 | +======= |
4247 | +html_theme = 'default' |
4248 | +>>>>>>> docs/conf.py |
4249 | |
4250 | # Theme options are theme-specific and customize the look and feel of a theme |
4251 | # further. For a list of options available for each theme, see the |
4252 | # documentation. |
4253 | +<<<<<<< docs/conf.py |
4254 | # |
4255 | # Due to the way disqus works, it's only going to work on |
4256 | # plainbox.readthedocs.org so only use it if building for readthedocs. |
4257 | @@ -137,6 +142,8 @@ html_theme_options = { |
4258 | 'show_disqus': 'true' if os.environ.get( |
4259 | "READTHEDOCS", None) == 'True' else '' |
4260 | } |
4261 | +======= |
4262 | +>>>>>>> docs/conf.py |
4263 | |
4264 | # Add any paths that contain custom themes here, relative to this directory. |
4265 | html_theme_path = ['_theme'] |
4266 | diff --git a/docs/dev/old.rst b/docs/dev/old.rst |
4267 | index 7921f68..9584888 100644 |
4268 | --- a/docs/dev/old.rst |
4269 | +++ b/docs/dev/old.rst |
4270 | @@ -144,6 +144,11 @@ dependencies) being added to the repository. |
4271 | Implementation issues |
4272 | --------------------- |
4273 | |
4274 | +<<<<<<< docs/dev/old.rst |
4275 | +======= |
4276 | +There are two issues that are known at this time: |
4277 | + |
4278 | +>>>>>>> docs/dev/old.rst |
4279 | * There is too much checkbox-specific knowledge which really belongs |
4280 | elsewhere. We are working to remove that so that non-checkbox jobs |
4281 | can be introduced later. There is a branch in progress that entirely |
4282 | @@ -153,6 +158,18 @@ Implementation issues |
4283 | that was previously internal (most notably a way to add new jobs and |
4284 | resources). |
4285 | |
4286 | +<<<<<<< docs/dev/old.rst |
4287 | +======= |
4288 | +* The way jobs are currently selected is unfortunate because of local jobs |
4289 | + that can add new jobs to the system. This causes considerable complexity |
4290 | + at the application level where the application must check if each |
4291 | + executed job is a 'local' job and re-compute the desired_job_list. This |
4292 | + should be replaced by a matcher function that can be passed to |
4293 | + SessionState once so that desired_job_list is re-evaluated internally |
4294 | + whenever job_list changes. |
4295 | + |
4296 | + |
4297 | +>>>>>>> docs/dev/old.rst |
4298 | :class:`~plainbox.impl.job.JobDefinition` |
4299 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
4300 | |
4301 | @@ -200,6 +217,52 @@ plugin == "manual" |
4302 | This value is used for fully manual jobs. It has no special handling in the core |
4303 | apart from requiring a human-provided outcome (pass/fail classification) |
4304 | |
4305 | +<<<<<<< docs/dev/old.rst |
4306 | +======= |
4307 | +.. _local: |
4308 | + |
4309 | +plugin == "local" |
4310 | +################# |
4311 | + |
4312 | +This value is used for special job generator jobs. The output of such jobs is |
4313 | +interpreted as additional jobs and is identical in effect to loading such jobs |
4314 | +from a job definition file. |
4315 | + |
4316 | +There are two practical uses for such jobs: |
4317 | + |
4318 | +* Some local jobs are used to generate a number of jobs for each object. |
4319 | + This is needed where the tested machine may have a number of such objects |
4320 | + and each requires unique testing. A good example is a computer where all |
4321 | + network tests are explicitly "instantiated" for each network card |
4322 | + present. |
4323 | + |
4324 | + This is a valid use case but is rather unfortunate for architecture of |
4325 | + Plainbox and there is a desire to replace it with equally-expressive |
4326 | + pattern jobs. The advantage is that unlike local jobs (which cannot be |
4327 | + "discovered" without enduring any potential side effects that may be |
4328 | + caused by the job script command) pattern jobs would allow the core to |
4329 | + determine the names of jobs that can be generated and, for example, |
4330 | + automatically determine that a pattern job needs to be executed as a |
4331 | + dependency of a phantom (yet undetermined) job with a given name. |
4332 | + |
4333 | + The solution with "pattern" jobs may be executed in future phases of |
4334 | + Plainbox development. Currently there is no support for that at all. |
4335 | + |
4336 | + Currently Plainbox cannot determine job dependencies across local jobs. |
4337 | + That is, unless a local job is explicitly requested (in the desired job |
4338 | + list) Plainbox will not be able to run a job that is generated by a local |
4339 | + job at all and will treat it as if that job never existed. |
4340 | + |
4341 | +* Some local jobs are used to create a form of informal "category". |
4342 | + Typically all such jobs have a leading and trailing double underscore, |
4343 | + for example '__audio__'. This is currently being used by Checkbox for |
4344 | + building a hierarchical tree of tests that the user may select. |
4345 | + |
4346 | + Since this has the same flaws as described above (for pattern jobs) it |
4347 | + will likely be replaced by an explicit category field that can be |
4348 | + specified each job. |
4349 | + |
4350 | +>>>>>>> docs/dev/old.rst |
4351 | plugin == "resource" |
4352 | #################### |
4353 | |
4354 | diff --git a/docs/dev/trusted-launcher.rst b/docs/dev/trusted-launcher.rst |
4355 | index a0cc565..6e5a6ec 100644 |
4356 | --- a/docs/dev/trusted-launcher.rst |
4357 | +++ b/docs/dev/trusted-launcher.rst |
4358 | @@ -145,7 +145,11 @@ Usage |
4359 | .. code-block:: text |
4360 | |
4361 | plainbox-trusted-launcher-1 [-h] (--hash HASH | --warmup) |
4362 | +<<<<<<< docs/dev/trusted-launcher.rst |
4363 | [--via GENERATOR-JOB-HASH] |
4364 | +======= |
4365 | + [--via LOCAL-JOB-HASH] |
4366 | +>>>>>>> docs/dev/trusted-launcher.rst |
4367 | [NAME=VALUE [NAME=VALUE ...]] |
4368 | |
4369 | positional arguments: |
4370 | @@ -156,7 +160,11 @@ Usage |
4371 | --hash HASH job hash to match |
4372 | --warmup Return immediately, only useful when used with |
4373 | pkexec(1) |
4374 | +<<<<<<< docs/dev/trusted-launcher.rst |
4375 | --via GENERATOR-JOB-HASH Generator job hash to use to match the generated job |
4376 | +======= |
4377 | + --via LOCAL-JOB-HASH Local job hash to use to match the generated job |
4378 | +>>>>>>> docs/dev/trusted-launcher.rst |
4379 | |
4380 | .. note:: |
4381 | |
4382 | @@ -185,11 +193,25 @@ thanks to the installed policy file the authentication will be kept. |
4383 | Special case of jobs using the Checkbox local plugin |
4384 | ---------------------------------------------------- |
4385 | |
4386 | +<<<<<<< docs/dev/trusted-launcher.rst |
4387 | For jobs generated from resources jobs (e.g. |
4388 | disk/read_performance.*) the trusted launcher is started with ``--via`` meaning |
4389 | that we have to first eval a generator job to find a hash match. Once a match is |
4390 | +======= |
4391 | +For jobs generated from :ref:`local <local>` jobs (e.g. |
4392 | +disk/read_performance.*) the trusted launcher is started with ``--via`` meaning |
4393 | +that we have to first eval a local job to find a hash match. Once a match is |
4394 | +>>>>>>> docs/dev/trusted-launcher.rst |
4395 | found, the job command is executed. |
4396 | |
4397 | .. code-block:: bash |
4398 | |
4399 | +<<<<<<< docs/dev/trusted-launcher.rst |
4400 | $ pkexec plainbox-trusted-launcher-1 --hash JOB-HASH --via GENERATOR-JOB-HASH |
4401 | +======= |
4402 | + $ pkexec plainbox-trusted-launcher-1 --hash JOB-HASH --via LOCAL-JOB-HASH |
4403 | + |
4404 | +.. note:: |
4405 | + |
4406 | + it will obviously fail if any local job can ever generate another local job. |
4407 | +>>>>>>> docs/dev/trusted-launcher.rst |
4408 | diff --git a/docs/glossary.rst b/docs/glossary.rst |
4409 | index 212d9a2..8d22fef 100644 |
4410 | --- a/docs/glossary.rst |
4411 | +++ b/docs/glossary.rst |
4412 | @@ -46,11 +46,20 @@ Glossary |
4413 | necessary for end-user work. ``plainbox`` is usually installed |
4414 | explicitly if needed. |
4415 | |
4416 | +<<<<<<< docs/glossary.rst |
4417 | test plan |
4418 | |
4419 | Test plans are text files used by Checkbox to select jobs for |
4420 | execution. They can include simple regular expressions to match and |
4421 | pick many similar jobs at once. |
4422 | +======= |
4423 | + whitelist |
4424 | + |
4425 | + Whitelists are text files used by Checkbox to select jobs for |
4426 | + execution. They can include simple regular expressions to match and |
4427 | + pick many similar jobs at once. For more information see |
4428 | + :doc:`Checkbox Whitelist Files <author/whitelists>` |
4429 | +>>>>>>> docs/glossary.rst |
4430 | |
4431 | job |
4432 | |
4433 | @@ -62,7 +71,11 @@ Glossary |
4434 | |
4435 | provider |
4436 | |
4437 | +<<<<<<< docs/glossary.rst |
4438 | A container for jobs, test plans, private executables and data. |
4439 | +======= |
4440 | + A container for jobs, whitelists, private executables and data. |
4441 | +>>>>>>> docs/glossary.rst |
4442 | Providers are the foundation of Plainbox as they *provide* all of the |
4443 | content. Providers can be created and managed by any entity, separately |
4444 | from the Checkbox project. |
4445 | @@ -95,7 +108,11 @@ Glossary |
4446 | attachment |
4447 | |
4448 | Attachments are a special type of a Job that can creates an attachment |
4449 | +<<<<<<< docs/glossary.rst |
4450 | record in the submission reports. They are commonly used to include |
4451 | +======= |
4452 | + record in the submission.xml file. They are commonly used to include |
4453 | +>>>>>>> docs/glossary.rst |
4454 | basic system information files and output of certain commands which can |
4455 | aid in system certification. |
4456 | |
4457 | diff --git a/docs/manpages/plainbox-dev-analyze.rst b/docs/manpages/plainbox-dev-analyze.rst |
4458 | index 23fc999..6bcc0c1 100644 |
4459 | --- a/docs/manpages/plainbox-dev-analyze.rst |
4460 | +++ b/docs/manpages/plainbox-dev-analyze.rst |
4461 | @@ -15,6 +15,16 @@ plainbox-dev-analyze (1) |
4462 | and the command prints nothing at all) to inspect certain aspects of the |
4463 | hypothetical session |
4464 | |
4465 | +<<<<<<< docs/manpages/plainbox-dev-analyze.rst |
4466 | +======= |
4467 | + The only exception to the rule above is the ``--run-local`` option. With that |
4468 | + option all local jobs and their dependencies *are* started. This is |
4469 | + technically required to correctly emulate the behavior of ``plainbox run`` |
4470 | + that does so unconditionally. Still, local jobs can cause harm so don't run |
4471 | + untrusted code this way (the author of this man page recalls one local job |
4472 | + that ran ``sudo reboot`` to measure bootchart data) |
4473 | + |
4474 | +>>>>>>> docs/manpages/plainbox-dev-analyze.rst |
4475 | Report Types |
4476 | ============ |
4477 | |
4478 | @@ -67,11 +77,19 @@ plainbox-dev-analyze (1) |
4479 | always includes additional jobs (such as resource jobs and other |
4480 | dependencies) |
4481 | |
4482 | +<<<<<<< docs/manpages/plainbox-dev-analyze.rst |
4483 | The run list is of great importance. Most of the time the test operator |
4484 | will see tests in precisely this order. The only exception is that some |
4485 | test applications choose to pre-run generator jobs (resources). Still, if |
4486 | your job ordering is wrong in any way, inspecting the run list is the best |
4487 | way to debug the problem. |
4488 | +======= |
4489 | + The run list is of great importance. Most of the time the test operator will |
4490 | + see tests in precisely this order. The only exception is that some test |
4491 | + applications choose to pre-run local jobs. Still, if your job ordering is |
4492 | + wrong in any way, inspecting the run list is the best way to debug the |
4493 | + problem. |
4494 | +>>>>>>> docs/manpages/plainbox-dev-analyze.rst |
4495 | |
4496 | See Also |
4497 | ======== |
4498 | diff --git a/docs/manpages/plainbox-exporter-units.rst b/docs/manpages/plainbox-exporter-units.rst |
4499 | index df7172c..2b0791b 100644 |
4500 | --- a/docs/manpages/plainbox-exporter-units.rst |
4501 | +++ b/docs/manpages/plainbox-exporter-units.rst |
4502 | @@ -120,4 +120,40 @@ The provider shipping such unit can be as follow:: |
4503 | └── exporters.pxu |
4504 | |
4505 | Note that exporters.pxu is not strictly needed to store the exporter units, but |
4506 | +<<<<<<< docs/manpages/plainbox-exporter-units.rst |
4507 | keeping them in a dedicated file is a good practice. |
4508 | +======= |
4509 | +keeping them in a dedidated file is a good practice. |
4510 | + |
4511 | +How to use exporter units? |
4512 | +-------------------------- |
4513 | + |
4514 | +In order to call an exporter unit from provider foo, you just need to add the |
4515 | +unit id to the cli or the gui launcher in the exporter section: |
4516 | + |
4517 | +Example of a gui launcher: |
4518 | + |
4519 | + #!/usr/bin/checkbox-gui |
4520 | + |
4521 | + [welcome] |
4522 | + title = "Foo" |
4523 | + text = "bar" |
4524 | + |
4525 | + [exporter] |
4526 | + HTML = "com.foo.bar::my_html" |
4527 | + |
4528 | +Example of a cli launcher: |
4529 | + |
4530 | + #!/usr/bin/env checkbox-launcher |
4531 | + [welcome] |
4532 | + text = Foo |
4533 | + |
4534 | + [suite] |
4535 | + whitelist_filter = ^.*$ |
4536 | + whitelist_selection = ^default$ |
4537 | + |
4538 | + [exporter] |
4539 | + com.foo.bar::my_html |
4540 | + com.foo.bar::my_json |
4541 | + com.foo.baz::my_html |
4542 | +>>>>>>> docs/manpages/plainbox-exporter-units.rst |
4543 | diff --git a/docs/manpages/plainbox-file-units.rst b/docs/manpages/plainbox-file-units.rst |
4544 | index 3c5ddbd..d8da24e 100644 |
4545 | --- a/docs/manpages/plainbox-file-units.rst |
4546 | +++ b/docs/manpages/plainbox-file-units.rst |
4547 | @@ -36,6 +36,12 @@ There are two fields that are used by the file unit: |
4548 | 'unit-source': |
4549 | The file is a source of unit definitions. Currently this is the only |
4550 | actually implemented value. |
4551 | +<<<<<<< docs/manpages/plainbox-file-units.rst |
4552 | +======= |
4553 | + |
4554 | + 'legacy-whitelist': |
4555 | + This file is a legacy whitelist. |
4556 | +>>>>>>> docs/manpages/plainbox-file-units.rst |
4557 | |
4558 | 'script': |
4559 | This file is an architecture independent executable. |
4560 | @@ -57,4 +63,8 @@ There are two fields that are used by the file unit: |
4561 | This file contains copyright and licensing information. |
4562 | |
4563 | 'docs': |
4564 | +<<<<<<< docs/manpages/plainbox-file-units.rst |
4565 | + This file contains documentation. |
4566 | +======= |
4567 | This file contains documentation. |
4568 | +>>>>>>> docs/manpages/plainbox-file-units.rst |
4569 | diff --git a/docs/manpages/plainbox-job-units.rst b/docs/manpages/plainbox-job-units.rst |
4570 | index 08e8fd1..d33c2d8 100644 |
4571 | --- a/docs/manpages/plainbox-job-units.rst |
4572 | +++ b/docs/manpages/plainbox-job-units.rst |
4573 | @@ -56,6 +56,12 @@ Following fields may be used by the job unit: |
4574 | test's outcome. This is essentially a manual job with a command. |
4575 | :attachment: jobs whose command output will be attached to the |
4576 | test report or submission. |
4577 | +<<<<<<< docs/manpages/plainbox-job-units.rst |
4578 | +======= |
4579 | + :local: a job whose command output needs to be in Checkbox job |
4580 | + format. Jobs output by a local job will be added to the set of |
4581 | + available jobs to be run. |
4582 | +>>>>>>> docs/manpages/plainbox-job-units.rst |
4583 | :resource: A job whose command output results in a set of rfc822 |
4584 | records, containing key/value pairs, and that can be used in other |
4585 | jobs' ``requires`` expressions. |
4586 | diff --git a/docs/manpages/plainbox-run.rst b/docs/manpages/plainbox-run.rst |
4587 | index 3363aa8..b3a6432 100644 |
4588 | --- a/docs/manpages/plainbox-run.rst |
4589 | +++ b/docs/manpages/plainbox-run.rst |
4590 | @@ -74,6 +74,47 @@ plainbox-run (1) |
4591 | |
4592 | plainbox run -i '.*::foo' -i '.*::bar' |
4593 | |
4594 | +<<<<<<< docs/manpages/plainbox-run.rst |
4595 | +======= |
4596 | + Selecting jobs with whitelists |
4597 | + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
4598 | + |
4599 | + The second mechanism is the ``--whitelist WHITELIST`` command-line option. |
4600 | + WhiteLists (or test plans, which is somewhat easier to relate to). |
4601 | + Whitelists are simple text files composed of a list of regular expressions, |
4602 | + identical to those that may be passed with the ``-i`` option. |
4603 | + |
4604 | + Unlike the ``-i`` option though, there are two kinds of whitelists. |
4605 | + Standalone whitelists are not associated with any Plainbox Provider. Such |
4606 | + whitelists can be distributed entirely separately from any other component |
4607 | + and thus have no association with any namespace. |
4608 | + |
4609 | + Therefore, be fully qualified, each pattern must include both the namespace |
4610 | + and the partial identifier components. For example, this is a valid, fully |
4611 | + quallified whitelist:: |
4612 | + |
4613 | + com.canonical.plainbox::stub/.* |
4614 | + |
4615 | + It will unambiguously select some of the jobs from the special, internal |
4616 | + StubBox provider that is built into Plainbox. It can be saved under any |
4617 | + filename and stored in any directory and it will always select the same set |
4618 | + of jobs. |
4619 | + |
4620 | + In contrast, whitelists that are associated with a particular provider, by |
4621 | + being stored in the per-provider ``whitelists/`` directory, carry an |
4622 | + implicit namespace. Such whitelists are typically written without |
4623 | + mentioning the namespace component. |
4624 | + |
4625 | + For example, the same "stub/.*" pattern can be abbreviated to:: |
4626 | + |
4627 | + stub/.* |
4628 | + |
4629 | + Typically this syntax is used in all whitelists specific to a particular |
4630 | + provider unless the provider maintainer explicitly wants to include a job |
4631 | + from another namespace (for example, one of the well-known Checkbox job |
4632 | + definitions). |
4633 | + |
4634 | +>>>>>>> docs/manpages/plainbox-run.rst |
4635 | GENERATED JOBS |
4636 | ============== |
4637 | |
4638 | @@ -87,7 +128,32 @@ plainbox-run (1) |
4639 | storage devices) and then duplicate each of the store specific tests so |
4640 | that all devices are tested separately. |
4641 | |
4642 | +<<<<<<< docs/manpages/plainbox-run.rst |
4643 | One limitation is that jobs cannot override existing definitions. |
4644 | +======= |
4645 | + At this time jobs can be generated only from jobs using the plugin type |
4646 | + `local`. Jobs of this kind are expected to print fully conforming job |
4647 | + definitions on stdout. Generated jobs cause a few complexities and one |
4648 | + limitation that is currently enforced is that generated jobs cannot |
4649 | + generate additional jobs if any of the affected jobs need to run as another |
4650 | + user. |
4651 | + |
4652 | + Another limitation is that jobs cannot override existing definitions. |
4653 | + |
4654 | + Creating Parent-Child Association |
4655 | + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
4656 | + |
4657 | + A relatively niche and legacy feature of generated jobs is to print a |
4658 | + verbatim copy of existing job definitions from a ``local`` job definition |
4659 | + named afer a generic testing theme or category. For example the Checkbox |
4660 | + job definition ``__wireless__`` prints, with the help of ``cat`` (1), all |
4661 | + of the job definitions defined in the file ``wireless.txt``. |
4662 | + |
4663 | + This behavior is special-cased not to cause redefinition errors. Instead, |
4664 | + existing definitions gain the ``via`` attribute that links them to the |
4665 | + generator job. This feature is used by derivative application such as |
4666 | + Checkbox. Plainbox is not using it at this time. |
4667 | +>>>>>>> docs/manpages/plainbox-run.rst |
4668 | |
4669 | RESUMING |
4670 | ======== |
4671 | @@ -132,10 +198,17 @@ plainbox-run (1) |
4672 | |
4673 | Some formats are more useful than others in that they are capable of |
4674 | transferring more of the internal state. Depending on your application you |
4675 | +<<<<<<< docs/manpages/plainbox-run.rst |
4676 | may wish to choose the most generic format (tar.xz) and process it further |
4677 | with additional tools, choose the most basic format (text) just to get a |
4678 | simple summary of the results or lastly choose one of the two specialized |
4679 | formats (xlsx and html) that are specific to the Checkbox workflow. |
4680 | +======= |
4681 | + may wish to choose the most generic format (json) and process it further |
4682 | + with additional tools, choose the most basic format (text) just to get a |
4683 | + simple summary of the results or lastly choose one of the two specialized |
4684 | + formats (xml and html) that are specific to the Checkbox workflow. |
4685 | +>>>>>>> docs/manpages/plainbox-run.rst |
4686 | |
4687 | Out of the box the following exporters are supported: |
4688 | |
4689 | @@ -171,7 +244,11 @@ plainbox-run (1) |
4690 | xlsx |
4691 | ---- |
4692 | |
4693 | +<<<<<<< docs/manpages/plainbox-run.rst |
4694 | This exporter creates a standalone .xlsx (OOXML format for Microsoft Excel) |
4695 | +======= |
4696 | + This exporter creates a standalone .xlsx (XML format for Microsoft Excel) |
4697 | +>>>>>>> docs/manpages/plainbox-run.rst |
4698 | file that contains a human-readable test report. It is quit similar to the |
4699 | HTML report but it is easier to edit. It is useful for communicating with |
4700 | other humans and since it is entirely standalone and off-line it can be |
4701 | @@ -179,6 +256,20 @@ plainbox-run (1) |
4702 | |
4703 | It depends on python3-xlsxwriter package |
4704 | |
4705 | +<<<<<<< docs/manpages/plainbox-run.rst |
4706 | +======= |
4707 | + hexr |
4708 | + ---- |
4709 | + |
4710 | + This exporter creates a rather confusingly named XML document only |
4711 | + applicable for internal Canonical Hardware Certification Team workflow. |
4712 | + |
4713 | + It is not a generic XML representation of test results and instead it |
4714 | + carries quite a few legacy constructs that are only retained for |
4715 | + compatibility with other internal tools. If you want generic processing |
4716 | + look for JSON instead. |
4717 | + |
4718 | +>>>>>>> docs/manpages/plainbox-run.rst |
4719 | Selecting Exporter Options |
4720 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ |
4721 | |
4722 | @@ -243,24 +334,51 @@ plainbox-run (1) |
4723 | Exported data will include comments added by the test operator to each |
4724 | job result that has them. |
4725 | |
4726 | +<<<<<<< docs/manpages/plainbox-run.rst |
4727 | with-job-hash: |
4728 | Exported data will include the ``hash`` attribute alongside each job |
4729 | result. The hash attribute is the checksum of the job definition's |
4730 | data. |
4731 | +======= |
4732 | + with-job-via: |
4733 | + Exported data will include the ``via`` attribute alongside each job |
4734 | + result. The via attribute contains the checksum of the job definition |
4735 | + that generated a particular job definition. This is useful for tracking |
4736 | + jobs generated by jobs with the plugin type `local`. |
4737 | + |
4738 | + with-job-hash: |
4739 | + Exported data will include the ``hash`` attribute alongside each job |
4740 | + result. The hash attribute is the checksum of the job definition's |
4741 | + data. It can be useful alongside with `with-job-via`. |
4742 | +>>>>>>> docs/manpages/plainbox-run.rst |
4743 | |
4744 | machine-json: |
4745 | The generated JSON document will be minimal (devoid of any optional |
4746 | whitespace). This option is best to be used if the result is not |
4747 | intended to be read by humans as it saves some space. |
4748 | |
4749 | +<<<<<<< docs/manpages/plainbox-run.rst |
4750 | text |
4751 | +======= |
4752 | + rfc822 |
4753 | +>>>>>>> docs/manpages/plainbox-run.rst |
4754 | ------ |
4755 | |
4756 | All of the options have the same meaning as for the `json` exporter: |
4757 | `with-io-log`, `squash-io-log`, `flatten-io-log`, `with-run-list`, |
4758 | `with-job-list`, `with-resource-map`, `with-job-defs`, `with-attachments`, |
4759 | +<<<<<<< docs/manpages/plainbox-run.rst |
4760 | `with-comments`, `with-job-hash`. The only exception is the `machine-json` |
4761 | option which doesn't exist for this exporter. |
4762 | +======= |
4763 | + `with-comments`, `with-job-via`, `with-job-hash`. The only exception is |
4764 | + the `machine-json` option which doesn't exist for this exporter. |
4765 | + |
4766 | + text |
4767 | + ---- |
4768 | + |
4769 | + Same as with rfc822. |
4770 | +>>>>>>> docs/manpages/plainbox-run.rst |
4771 | |
4772 | xlsx |
4773 | ---- |
4774 | @@ -280,6 +398,18 @@ plainbox-run (1) |
4775 | with-text-attachments: |
4776 | Exported spreadsheet will include text attachments on a separate sheet |
4777 | |
4778 | +<<<<<<< docs/manpages/plainbox-run.rst |
4779 | +======= |
4780 | + xml |
4781 | + --- |
4782 | + |
4783 | + client-name: |
4784 | + This option allows clients to override the name of the application |
4785 | + generating the XML document. By default that name is `plainbox`. To |
4786 | + use this option pass ``--output-options client-name=other-name`` |
4787 | + command-line option. |
4788 | + |
4789 | +>>>>>>> docs/manpages/plainbox-run.rst |
4790 | TRANSPORTING RESULTS |
4791 | ==================== |
4792 | |
4793 | @@ -298,12 +428,32 @@ plainbox-run (1) |
4794 | |
4795 | Plainbox comes equipped with the following transports: |
4796 | |
4797 | +<<<<<<< docs/manpages/plainbox-run.rst |
4798 | certification |
4799 | ^^^^^^^^^^^^^ |
4800 | |
4801 | This transport can send the results exported using the ``tar`` exporter to |
4802 | the Canonical Certification Website (https://certification.canonical.com). |
4803 | |
4804 | +======= |
4805 | + launchpad |
4806 | + ^^^^^^^^^ |
4807 | + |
4808 | + This transport can send the results exported using ``xml`` exporter to the |
4809 | + Launchpad Hardware Database. This is a little-known feature offered by the |
4810 | + https://launchpad.net/ website. |
4811 | + |
4812 | + certification |
4813 | + ^^^^^^^^^^^^^ |
4814 | + |
4815 | + This transport can send the results exported using the ``xml`` exporter to |
4816 | + the Canonical Certification Website (https://certification.canonical.com). |
4817 | + |
4818 | + This transport is of little use to anyone but the Canonical Hardware |
4819 | + Certification Team that also maintains Plainbox and Checkbox but it is |
4820 | + mentioned here for completeness. |
4821 | + |
4822 | +>>>>>>> docs/manpages/plainbox-run.rst |
4823 | See Also |
4824 | ======== |
4825 | |
4826 | diff --git a/docs/manpages/plainbox-test-plan-units.rst b/docs/manpages/plainbox-test-plan-units.rst |
4827 | index 6857100..b04b135 100644 |
4828 | --- a/docs/manpages/plainbox-test-plan-units.rst |
4829 | +++ b/docs/manpages/plainbox-test-plan-units.rst |
4830 | @@ -99,10 +99,18 @@ copy such constructs when working on a new test plan from scratch |
4831 | common and most test plans used by Checkbox actually look like that. |
4832 | |
4833 | - You can use regular expressions to select many tests at the same time. |
4834 | +<<<<<<< docs/manpages/plainbox-test-plan-units.rst |
4835 | This is the only way to select generated jobs (created by template |
4836 | units). Please remember that the dot character has a special meaning so |
4837 | unless you actually want to match *any character* escape the dot with |
4838 | the backslash character (\\). |
4839 | +======= |
4840 | + This is the only way to select generated jobs (created either by |
4841 | + template units or by job definitions using the legacy 'local' plugin |
4842 | + type). Please remember that the dot character has a special meaning |
4843 | + so unless you actually want to match *any character* escape the dot |
4844 | + with the backslash character (\\). |
4845 | +>>>>>>> docs/manpages/plainbox-test-plan-units.rst |
4846 | |
4847 | Regardless of if you use patterns or literal job identifiers you can use |
4848 | their fully qualified name (the one that includes the namespace they reside |
4849 | @@ -148,7 +156,11 @@ copy such constructs when working on a new test plan from scratch |
4850 | |
4851 | Note that each entry in the bootstrap_include section must be a valid job |
4852 | identifier and cannot be a regular expression pattern. |
4853 | +<<<<<<< docs/manpages/plainbox-test-plan-units.rst |
4854 | Also note that only resource jobs are allowed in this section. |
4855 | +======= |
4856 | + Also note that only local and resource jobs are allowed in this section. |
4857 | +>>>>>>> docs/manpages/plainbox-test-plan-units.rst |
4858 | |
4859 | ``exclude``: |
4860 | A multi-line list of job identifiers or patterns matching such identifiers |
4861 | diff --git a/docs/manpages/plainbox-trusted-launcher-1.rst b/docs/manpages/plainbox-trusted-launcher-1.rst |
4862 | index 77cdfeb..e609736 100644 |
4863 | --- a/docs/manpages/plainbox-trusted-launcher-1.rst |
4864 | +++ b/docs/manpages/plainbox-trusted-launcher-1.rst |
4865 | @@ -77,8 +77,18 @@ The following environment variables *DO NOT* affect ``plainbox-trusted-launcher- |
4866 | Bugs |
4867 | ==== |
4868 | |
4869 | +<<<<<<< docs/manpages/plainbox-trusted-launcher-1.rst |
4870 | The launcher is somewhat inefficient, in that it has to re-run all of the |
4871 | dependencies of the generator job over and over. Ideally those would be cached, |
4872 | +======= |
4873 | +Currently it is impossible to use ``plainbox-trusted-launcher-1`` with a |
4874 | +``local`` job needs to run as root, that generates another ``local`` job that |
4875 | +needs to run as root, to generate any additional jobs that also need to run as |
4876 | +root. In other words, only one-level job generation is supported. |
4877 | + |
4878 | +The launcher is somewhat inefficient, in that it has to re-run all of the |
4879 | +dependencies of the ``local`` job over and over. Ideally those would be cached, |
4880 | +>>>>>>> docs/manpages/plainbox-trusted-launcher-1.rst |
4881 | per-session, but that would significantly increase the complexity of the code |
4882 | running as root. |
4883 | |
4884 | diff --git a/docs/usage.rst b/docs/usage.rst |
4885 | index e2f2f0a..6f698b5 100644 |
4886 | --- a/docs/usage.rst |
4887 | +++ b/docs/usage.rst |
4888 | @@ -47,12 +47,31 @@ To list all known jobs run: |
4889 | |
4890 | plainbox dev special --list-jobs |
4891 | |
4892 | +<<<<<<< docs/usage.rst |
4893 | +======= |
4894 | +Running a white list |
4895 | +^^^^^^^^^^^^^^^^^^^^ |
4896 | + |
4897 | +To run a :term:`whitelist` pass the ``--whitelist`` or ``-w`` option. |
4898 | + |
4899 | +For example, to run the default white list run: |
4900 | + |
4901 | +.. code-block:: bash |
4902 | + |
4903 | + $ plainbox run -w /path/to/some/file.whitelist |
4904 | + |
4905 | +>>>>>>> docs/usage.rst |
4906 | Saving test results |
4907 | ^^^^^^^^^^^^^^^^^^^ |
4908 | |
4909 | Anything that Plainbox captures and stores during test execution can be |
4910 | +<<<<<<< docs/usage.rst |
4911 | exported to a file using the exporter system. The three most commonly used |
4912 | exporters are tar.xz, html and xlsx. |
4913 | +======= |
4914 | +exported to a file using the exporter system. The two most commonly used |
4915 | +exporters are JSON (versatile and general) and XML (for internal Canonical use). |
4916 | +>>>>>>> docs/usage.rst |
4917 | |
4918 | JSON Exporter |
4919 | ------------- |
4920 | @@ -61,7 +80,11 @@ To generate a JSON file with all of the internally available data (for storage, |
4921 | processing or other automation) you will need to pass three additional |
4922 | arguments to ``plainbox run``: |
4923 | |
4924 | +<<<<<<< docs/usage.rst |
4925 | #. ``--output-format=com.canonical.com.canonical.plainbox::json`` |
4926 | +======= |
4927 | +#. ``--output-format=com.canonical.plainbox::json`` |
4928 | +>>>>>>> docs/usage.rst |
4929 | #. ``--output-options=OPTION1,OPTION2`` where *OPTIONx* are option names. |
4930 | #. ``--output-file=NAME`` where *NAME* is a file name. |
4931 | |
4932 | @@ -70,7 +93,27 @@ exporter options can be specified, separated with commas. |
4933 | |
4934 | .. code-block:: bash |
4935 | |
4936 | +<<<<<<< docs/usage.rst |
4937 | $ plainbox run -i com.canonical.certification::foo --output-format=com.canonical.plainbox::json --output-file=results.json |
4938 | +======= |
4939 | + $ plainbox run --whitelist=/path/to/some/file.whitelist --output-format=com.canonical.plainbox::json --output-file=results.json |
4940 | + |
4941 | +XML Exporter |
4942 | +------------ |
4943 | + |
4944 | +To generate an XML file that can be sent to the :term:`certification website` |
4945 | +you need to pass two additional arguments to ``plainbox run``: |
4946 | + |
4947 | +#. ``--output-format=com.canonical.plainbox::hexr`` |
4948 | +#. ``--output-file=NAME`` where *NAME* is a file name |
4949 | + |
4950 | +For example, to get the default certification tests ready to be submitted |
4951 | +run this command: |
4952 | + |
4953 | +.. code-block:: bash |
4954 | + |
4955 | + $ plainbox run --whitelist=/path/to/some/file.whitelist --output-format=com.canonical.plainbox::hexr --output-file=submission.xml |
4956 | +>>>>>>> docs/usage.rst |
4957 | |
4958 | Other Exporters |
4959 | --------------- |
4960 | diff --git a/plainbox.egg-info/PKG-INFO b/plainbox.egg-info/PKG-INFO |
4961 | new file mode 100644 |
4962 | index 0000000..fafbf3f |
4963 | --- /dev/null |
4964 | +++ b/plainbox.egg-info/PKG-INFO |
4965 | @@ -0,0 +1,62 @@ |
4966 | +Metadata-Version: 1.1 |
4967 | +Name: plainbox |
4968 | +Version: 0.38.0 |
4969 | +Summary: Toolkit for software and hardware integration testing |
4970 | +Home-page: https://launchpad.net/plainbox/ |
4971 | +Author: Zygmunt Krynicki |
4972 | +Author-email: zygmunt.krynicki@canonical.com |
4973 | +License: GPLv3 |
4974 | +Description: PlainBox |
4975 | + ======== |
4976 | + |
4977 | + PlainBox is a toolkit consisting of python3 library, development tools, |
4978 | + documentation and examples. It is targeted at developers working on testing or |
4979 | + certification applications and authors creating tests for such applications. |
4980 | + |
4981 | + PlainBox can be used to both create simple and comprehensive test tools as well |
4982 | + as to develop and execute test jobs and test scenarios. It was created as a |
4983 | + refined and rewritten core of the CheckBox project. It has a well tested and |
4984 | + documented core, small but active development community and a collection of |
4985 | + associated projects that use it as a lower-level engine/back-end library. |
4986 | + |
4987 | + PlainBox has a novel approach to discovering (and probing) hardware and |
4988 | + software that is extensible and not hardwired into the system. It allows test |
4989 | + developers to express association between a particular test and the hardware, |
4990 | + software and configuration constraints that must be met for the test to execute |
4991 | + meaningfully. This feature, along with pluggable test definitions, makes |
4992 | + PlainBox flexible and applicable to many diverse testing situations, ranging |
4993 | + from mobile phones, traditional desktop computers, servers and up to testing |
4994 | + "cloud" installations. |
4995 | + |
4996 | + External Documentation Links |
4997 | + ============================ |
4998 | + |
4999 | + * `Using PlainBox <http://plainbox.readthedocs.org/en/latest/usage.html>`_ |
5000 | + * `Hacking on PlainBox <http://plainbox.readthedocs.org/en/latest/dev/index.html>`_ |