Merge branch 'master' of ssh://git.samba.org/data/git/samba
authorJeremy Allison <jra@samba.org>
Wed, 31 Mar 2010 17:01:03 +0000 (10:01 -0700)
committerJeremy Allison <jra@samba.org>
Wed, 31 Mar 2010 17:01:03 +0000 (10:01 -0700)
87 files changed:
lib/subunit/Apache-2.0 [new file with mode: 0644]
lib/subunit/BSD [new file with mode: 0644]
lib/subunit/COPYING [new file with mode: 0644]
lib/subunit/INSTALL [new file with mode: 0644]
lib/subunit/Makefile.am [new file with mode: 0644]
lib/subunit/NEWS [new file with mode: 0644]
lib/subunit/README
lib/subunit/c++/README [new file with mode: 0644]
lib/subunit/c++/SubunitTestProgressListener.cpp [new file with mode: 0644]
lib/subunit/c++/SubunitTestProgressListener.h [new file with mode: 0644]
lib/subunit/c/README [new file with mode: 0644]
lib/subunit/c/include/subunit/child.h [new file with mode: 0644]
lib/subunit/c/lib/child.c [new file with mode: 0644]
lib/subunit/c/tests/test_child.c [new file with mode: 0644]
lib/subunit/configure.ac [new file with mode: 0644]
lib/subunit/filters/subunit-filter [new file with mode: 0755]
lib/subunit/filters/subunit-ls [new file with mode: 0755]
lib/subunit/filters/subunit-notify [new file with mode: 0755]
lib/subunit/filters/subunit-stats [new file with mode: 0755]
lib/subunit/filters/subunit-tags [new file with mode: 0755]
lib/subunit/filters/subunit2gtk [new file with mode: 0755]
lib/subunit/filters/subunit2junitxml [new file with mode: 0755]
lib/subunit/filters/subunit2pyunit [new file with mode: 0755]
lib/subunit/libcppunit_subunit.pc.in [new file with mode: 0644]
lib/subunit/libsubunit.pc.in [new file with mode: 0644]
lib/subunit/perl/Makefile.PL.in [new file with mode: 0755]
lib/subunit/perl/lib/Subunit.pm [new file with mode: 0644]
lib/subunit/perl/lib/Subunit/Diff.pm [new file with mode: 0644]
lib/subunit/perl/subunit-diff [new file with mode: 0755]
lib/subunit/runtests.py [new file with mode: 0755]
lib/subunit/shell/README [new file with mode: 0644]
lib/subunit/shell/share/subunit.sh [new file with mode: 0644]
lib/subunit/shell/tests/test_function_output.sh [new file with mode: 0755]
lib/subunit/shell/tests/test_source_library.sh [new file with mode: 0755]
lib/subunit/update.sh [deleted file]
lib/testtools/HACKING [new file with mode: 0644]
lib/testtools/LICENSE [new file with mode: 0644]
lib/testtools/MANIFEST.in [new file with mode: 0644]
lib/testtools/MANUAL [new file with mode: 0644]
lib/testtools/Makefile [new file with mode: 0644]
lib/testtools/NEWS [new file with mode: 0644]
lib/testtools/README [new file with mode: 0644]
lib/testtools/setup.py [new file with mode: 0755]
lib/testtools/testtools/__init__.py [moved from lib/subunit/python/testtools/__init__.py with 100% similarity]
lib/testtools/testtools/content.py [moved from lib/subunit/python/testtools/content.py with 100% similarity]
lib/testtools/testtools/content_type.py [moved from lib/subunit/python/testtools/content_type.py with 100% similarity]
lib/testtools/testtools/matchers.py [moved from lib/subunit/python/testtools/matchers.py with 100% similarity]
lib/testtools/testtools/run.py [moved from lib/subunit/python/testtools/run.py with 100% similarity]
lib/testtools/testtools/runtest.py [moved from lib/subunit/python/testtools/runtest.py with 100% similarity]
lib/testtools/testtools/testcase.py [moved from lib/subunit/python/testtools/testcase.py with 100% similarity]
lib/testtools/testtools/testresult/__init__.py [moved from lib/subunit/python/testtools/testresult/__init__.py with 100% similarity]
lib/testtools/testtools/testresult/doubles.py [moved from lib/subunit/python/testtools/testresult/doubles.py with 100% similarity]
lib/testtools/testtools/testresult/real.py [moved from lib/subunit/python/testtools/testresult/real.py with 100% similarity]
lib/testtools/testtools/tests/__init__.py [moved from lib/subunit/python/testtools/tests/__init__.py with 100% similarity]
lib/testtools/testtools/tests/helpers.py [moved from lib/subunit/python/testtools/tests/helpers.py with 100% similarity]
lib/testtools/testtools/tests/test_content.py [moved from lib/subunit/python/testtools/tests/test_content.py with 100% similarity]
lib/testtools/testtools/tests/test_content_type.py [moved from lib/subunit/python/testtools/tests/test_content_type.py with 100% similarity]
lib/testtools/testtools/tests/test_matchers.py [moved from lib/subunit/python/testtools/tests/test_matchers.py with 100% similarity]
lib/testtools/testtools/tests/test_runtest.py [moved from lib/subunit/python/testtools/tests/test_runtest.py with 100% similarity]
lib/testtools/testtools/tests/test_testresult.py [moved from lib/subunit/python/testtools/tests/test_testresult.py with 100% similarity]
lib/testtools/testtools/tests/test_testsuite.py [moved from lib/subunit/python/testtools/tests/test_testsuite.py with 100% similarity]
lib/testtools/testtools/tests/test_testtools.py [moved from lib/subunit/python/testtools/tests/test_testtools.py with 100% similarity]
lib/testtools/testtools/testsuite.py [moved from lib/subunit/python/testtools/testsuite.py with 100% similarity]
lib/testtools/testtools/utils.py [moved from lib/subunit/python/testtools/utils.py with 100% similarity]
lib/torture/subunit.c
lib/torture/torture.c
lib/torture/torture.h
lib/update-external.sh [new file with mode: 0755]
selftest/Subunit.pm
selftest/filter-subunit [new file with mode: 0755]
selftest/filter-subunit.pl [deleted file]
selftest/format-subunit
selftest/selftest.pl
selftest/subunithelper.py
source3/Makefile.in
source3/smbd/process.c
source4/lib/ldb/tests/python/deletetest.py
source4/lib/ldb/tests/python/urgent_replication.py
source4/script/installmisc.sh
source4/scripting/python/config.mk
source4/scripting/python/samba_external/missing.py
source4/selftest/config.mk
source4/selftest/knownfail
source4/selftest/tests.sh
source4/torture/basic/denytest.c
source4/torture/raw/open.c
source4/torture/smbtorture.c

diff --git a/lib/subunit/Apache-2.0 b/lib/subunit/Apache-2.0
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/lib/subunit/BSD b/lib/subunit/BSD
new file mode 100644 (file)
index 0000000..fa130cd
--- /dev/null
@@ -0,0 +1,26 @@
+Copyright (c) Robert Collins and Subunit contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of Robert Collins nor the names of Subunit contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY ROBERT COLLINS AND SUBUNIT CONTRIBUTORS ``AS IS''
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/lib/subunit/COPYING b/lib/subunit/COPYING
new file mode 100644 (file)
index 0000000..3ba50f8
--- /dev/null
@@ -0,0 +1,36 @@
+Subunit is licensed under two licenses, the Apache License, Version 2.0 or the
+3-clause BSD License. You may use this project under either of these licenses
+- choose the one that works best for you.
+
+We require contributions to be licensed under both licenses. The primary
+difference between them is that the Apache license takes care of potential
+issues with Patents and other intellectual property concerns. This is
+important to Subunit as Subunit wants to be license compatible in a very 
+broad manner to allow reuse and incorporation into other projects.
+
+Generally every source file in Subunit needs a license grant under both these
+licenses.  As the code is shipped as a single unit, a brief form is used:
+----
+Copyright (c) [yyyy][,yyyy]* [name or 'Subunit Contributors']
+
+Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+license at the users choice. A copy of both licenses are available in the
+project source as Apache-2.0 and BSD. You may not use this file except in
+compliance with one of these two licences.
+
+Unless required by applicable law or agreed to in writing, software
+distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+license you chose for the specific language governing permissions and
+limitations under that license.
+----
+
+Code that has been incorporated into Subunit from other projects will
+naturally be under its own license, and will retain that license.
+
+A known list of such code is maintained here:
+* The python/iso8601 module by Michael Twomey, distributed under an MIT style
+  licence - see python/iso8601/LICENSE for details.
+* The runtests.py and python/subunit/tests/TestUtil.py module are GPL test
+  support modules. There are not installed by Subunit - they are only ever
+  used on the build machine.  Copyright 2004 Canonical Limited.
diff --git a/lib/subunit/INSTALL b/lib/subunit/INSTALL
new file mode 100644 (file)
index 0000000..79cf7c1
--- /dev/null
@@ -0,0 +1,25 @@
+To install subunit
+------------------
+
+Bootstrap::
+  autoreconf -vi
+Configure::
+  ./configure
+Install::
+  make install
+
+Dependencies
+------------
+
+* Python for the filters
+* 'testtools' (On Debian and Ubuntu systems the 'python-testtools' package,
+  the testtools package on pypi, or https://launchpad.net/testtools) for
+  the extended test API which permits attachments. Version 0.9.2 or newer is 
+  required. Of particular note, http://testtools.python-hosting.com/ is not
+  the testtools you want.
+* A C compiler for the C bindings
+* Perl for the Perl tools (including subunit-diff)
+* Check to run the subunit test suite.
+* python-gtk2 if you wish to use subunit2gtk
+* python-junitxml if you wish to use subunit2junitxml
+* pkg-config for configure detection of supporting libraries.
diff --git a/lib/subunit/Makefile.am b/lib/subunit/Makefile.am
new file mode 100644 (file)
index 0000000..716fa0f
--- /dev/null
@@ -0,0 +1,136 @@
+EXTRA_DIST =  \
+       .bzrignore \
+       Apache-2.0 \
+       BSD \
+       INSTALL \
+       Makefile.am \
+       NEWS \
+       README \
+       c++/README \
+       c/README \
+       c/check-subunit-0.9.3.patch \
+       c/check-subunit-0.9.5.patch \
+       c/check-subunit-0.9.6.patch \
+       perl/Makefile.PL.in \
+       perl/lib/Subunit.pm \
+       perl/lib/Subunit/Diff.pm \
+       perl/subunit-diff \
+       python/iso8601/LICENSE \
+       python/iso8601/README \
+       python/iso8601/README.subunit \
+       python/iso8601/setup.py \
+       python/iso8601/test_iso8601.py \
+       python/subunit/tests/TestUtil.py \
+       python/subunit/tests/__init__.py \
+       python/subunit/tests/sample-script.py \
+       python/subunit/tests/sample-two-script.py \
+       python/subunit/tests/test_chunked.py \
+       python/subunit/tests/test_details.py \
+       python/subunit/tests/test_progress_model.py \
+       python/subunit/tests/test_subunit_filter.py \
+       python/subunit/tests/test_subunit_stats.py \
+       python/subunit/tests/test_subunit_tags.py \
+       python/subunit/tests/test_tap2subunit.py \
+       python/subunit/tests/test_test_protocol.py \
+       python/subunit/tests/test_test_results.py \
+       runtests.py \
+       shell/README \
+       shell/share/subunit.sh \
+       shell/subunit-ui.patch \
+       shell/tests/test_function_output.sh \
+       shell/tests/test_source_library.sh
+
+ACLOCAL_AMFLAGS = -I m4
+
+include_subunitdir = $(includedir)/subunit
+
+dist_bin_SCRIPTS = \
+       filters/subunit-filter \
+       filters/subunit-ls \
+       filters/subunit-stats \
+       filters/subunit-tags \
+       filters/subunit2gtk \
+       filters/subunit2junitxml \
+       filters/subunit2pyunit \
+       filters/tap2subunit
+
+TESTS_ENVIRONMENT = SHELL_SHARE='$(top_srcdir)/shell/share/' PYTHONPATH='$(abs_top_srcdir)/python':${PYTHONPATH}
+TESTS = runtests.py $(check_PROGRAMS)
+
+## install libsubunit.pc
+pcdatadir = $(libdir)/pkgconfig
+pcdata_DATA = \
+       libsubunit.pc \
+       libcppunit_subunit.pc
+
+pkgpython_PYTHON = \
+       python/subunit/__init__.py \
+       python/subunit/chunked.py \
+       python/subunit/details.py \
+       python/subunit/iso8601.py \
+       python/subunit/progress_model.py \
+       python/subunit/run.py \
+       python/subunit/test_results.py
+
+lib_LTLIBRARIES = libsubunit.la
+lib_LTLIBRARIES +=  libcppunit_subunit.la
+
+include_subunit_HEADERS = \
+       c/include/subunit/child.h \
+       c++/SubunitTestProgressListener.h
+
+check_PROGRAMS = \
+       c/tests/test_child
+
+check_SCRIPTS = \
+       runtests.py
+
+libsubunit_la_SOURCES = \
+       c/lib/child.c \
+       c/include/subunit/child.h
+
+libcppunit_subunit_la_SOURCES = \
+       c++/SubunitTestProgressListener.cpp \
+       c++/SubunitTestProgressListener.h
+
+tests_LDADD = @CHECK_LIBS@ $(top_builddir)/libsubunit.la
+c_tests_test_child_CFLAGS = -I$(top_srcdir)/c/include $(SUBUNIT_CFLAGS) @CHECK_CFLAGS@
+c_tests_test_child_LDADD = $(tests_LDADD)
+
+
+all-local: perl/Makefile
+       $(MAKE) -C perl all
+
+check-local: perl/Makefile
+       $(MAKE) -C perl check
+
+clean-local:
+       find . -type f -name "*.pyc" -exec rm {} ';'
+       rm -f perl/Makefile
+
+# Remove perl dir for VPATH builds.
+distclean-local:
+       -rmdir perl > /dev/null
+       -rm perl/Makefile.PL > /dev/null
+
+install-exec-local: perl/Makefile
+       $(MAKE) -C perl install
+
+mostlyclean-local:
+       rm -rf perl/blib
+       rm -rf perl/pm_to_blib
+
+# 'uninstall' perl files during distcheck
+uninstall-local:
+       if [ "_inst" = `basename ${prefix}` ]; then \
+         $(MAKE) -C perl uninstall_distcheck; \
+           rm -f "$(DESTDIR)$(bindir)"/subunit-diff; \
+       fi
+
+# The default for MakeMaker; can be overridden by exporting
+INSTALLDIRS ?= site
+
+perl/Makefile: perl/Makefile.PL
+       mkdir -p perl
+       cd perl && perl Makefile.PL INSTALLDIRS=${INSTALLDIRS}
+       -rm perl/Makefile.old > /dev/null
diff --git a/lib/subunit/NEWS b/lib/subunit/NEWS
new file mode 100644 (file)
index 0000000..7c933c8
--- /dev/null
@@ -0,0 +1,174 @@
+---------------------
+subunit release notes
+---------------------
+
+NEXT (In development)
+---------------------
+
+BUG FIXES
+~~~~~~~~~
+
+* Fix incorrect reference to subunit_test_failf in c/README.
+  (Brad Hards, #524341)
+
+* Fix incorrect ordering of tags method parameters in TestResultDecorator. This
+  is purely cosmetic as the parameters are passed down with no interpretation.
+  (Robert Collins, #537611)
+
+0.0.5
+-----
+
+BUG FIXES
+~~~~~~~~~
+
+* make check was failing if subunit wasn't installed due to a missing include
+  path for the test program test_child.
+
+* make distcheck was failing due to a missing $(top_srcdir) rune.
+
+IMPROVEMENTS
+~~~~~~~~~~~~
+
+* New filter `subunit-notify` that will show a notification window with test 
+  statistics when the test run finishes.
+
+* subunit.run will now pipe its output to the command in the 
+  SUBUNIT_FORMATTER environment variable, if set.
+
+0.0.4
+-----
+
+BUG FIXES
+~~~~~~~~~
+
+* subunit2junitxml -f required a value, this is now fixed and -f acts as a
+  boolean switch with no parameter.
+
+* Building with autoconf 2.65 is now supported.
+
+
+0.0.3
+-----
+
+  CHANGES:
+  
+    * License change, by unanimous agreement of contributors to BSD/Apache
+      License Version 2.0. This makes Subunit compatible with more testing
+      frameworks.
+
+  IMPROVEMENTS:
+
+    * CPPUnit is now directly supported: subunit builds a cppunit listener
+      ``libcppunit-subunit``. 
+
+    * In the python API ``addExpectedFailure`` and ``addUnexpectedSuccess``
+      from python 2.7/3.1 are now supported. ``addExpectedFailure`` is
+      serialised as ``xfail``, and ``addUnexpectedSuccess`` as ``success``.
+      The ``ProtocolTestCase`` parser now calls outcomes using an extended
+      API that permits attaching arbitrary MIME resources such as text files
+      log entries and so on. This extended API is being developed with the
+      Python testing community, and is in flux. ``TestResult`` objects that
+      do not support the API will be detected and transparently downgraded
+      back to the regular Python unittest API.
+
+    * INSTALLDIRS can be set to control the perl MakeMaker 'INSTALLDIRS'
+      viarable when installing.
+
+    * Multipart test outcomes are tentatively supported; the exact protocol
+      for them, both serialiser and object is not yet finalised. Testers and
+      early adopters are sought. As part of this and also in an attempt to
+      provider a more precise focus on the wire protocol and toolchain, 
+      Subunit now depends on testtools (http://launchpad.net/testtools)
+      release 0.9.0 or newer.
+
+    * subunit2junitxml supports a new option, --forward which causes it
+      to forward the raw subunit stream in a similar manner to tee. This
+      is used with the -o option to both write a xml report and get some
+      other subunit filter to process the stream.
+
+    * The C library now has ``subunit_test_skip``.
+
+  BUG FIXES:
+
+    * Install progress_model.py correctly.
+
+    * Non-gcc builds will no longer try to use gcc specific flags.
+      (Thanks trondn-norbye)
+
+  API CHANGES:
+
+  INTERNALS:
+
+0.0.2
+-----
+
+  CHANGES:
+
+  IMPROVEMENTS:
+
+    * A number of filters now support ``--no-passthrough`` to cause all
+      non-subunit content to be discarded. This is useful when precise control
+      over what is output is required - such as with subunit2junitxml.
+
+    * A small perl parser is now included, and a new ``subunit-diff`` tool
+      using that is included. (Jelmer Vernooij)
+
+    * Subunit streams can now include optional, incremental lookahead
+      information about progress. This allows reporters to make estimates
+      about completion, when such information is available. See the README
+      under ``progress`` for more details.
+
+    * ``subunit-filter`` now supports regex filtering via ``--with`` and
+      ``without`` options. (Martin Pool)
+
+    * ``subunit2gtk`` has been added, a filter that shows a GTK summary of a
+      test stream.
+
+    * ``subunit2pyunit`` has a --progress flag which will cause the bzrlib
+      test reporter to be used, which has a textual progress bar. This requires
+      a recent bzrlib as a minor bugfix was required in bzrlib to support this.
+
+    * ``subunit2junitxml`` has been added. This filter converts a subunit
+      stream to a single JUnit style XML stream using the pyjunitxml
+      python library.
+
+    * The shell functions support skipping via ``subunit_skip_test`` now.
+
+  BUG FIXES:
+
+    * ``xfail`` outcomes are now passed to python TestResult's via
+      addExpectedFailure if it is present on the TestResult. Python 2.6 and
+      earlier which do not have this function will have ``xfail`` outcomes
+      passed through as success outcomes as earlier versions of subunit did.
+
+  API CHANGES:
+
+    * tags are no longer passed around in python via the ``TestCase.tags``
+      attribute. Instead ``TestResult.tags(new_tags, gone_tags)`` is called,
+      and like in the protocol, if called while a test is active only applies
+      to that test. (Robert Collins)
+
+    * ``TestResultFilter`` takes a new optional constructor parameter 
+      ``filter_predicate``.  (Martin Pool)
+
+    * When a progress: directive is encountered in a subunit stream, the
+      python bindings now call the ``progress(offset, whence)`` method on
+      ``TestResult``.
+
+    * When a time: directive is encountered in a subunit stream, the python
+      bindings now call the ``time(seconds)`` method on ``TestResult``.
+
+  INTERNALS:
+
+    * (python) Added ``subunit.test_results.AutoTimingTestResultDecorator``. Most
+      users of subunit will want to wrap their ``TestProtocolClient`` objects
+      in this decorator to get test timing data for performance analysis.
+
+    * (python) ExecTestCase supports passing arguments to test scripts.
+
+    * (python) New helper ``subunit.test_results.HookedTestResultDecorator``
+      which can be used to call some code on every event, without having to
+      implement all the event methods.
+
+    * (python) ``TestProtocolClient.time(a_datetime)`` has been added which
+      causes a timestamp to be output to the stream.
index c657992c7a52c9960503be6e7e3a23dbee63f25b..9740d013a53487cff602abe9f06867a588fa9ad9 100644 (file)
@@ -1,7 +1,212 @@
-This directory contains some helper code for the Subunit protocol. It is
-a partial import of the code from the upstream subunit project, which can 
-be found at https://launchpad.net/subunit.
 
-To update the snapshot, run update.sh in this directory. When making changes 
-here, please also submit them upstream - otherwise they'll be gone by the 
-next time we import subunit.
+  subunit: A streaming protocol for test results
+  Copyright (C) 2005-2009 Robert Collins <robertc@robertcollins.net>
+
+  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+  license at the users choice. A copy of both licenses are available in the
+  project source as Apache-2.0 and BSD. You may not use this file except in
+  compliance with one of these two licences.
+  
+  Unless required by applicable law or agreed to in writing, software
+  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+  license you chose for the specific language governing permissions and
+  limitations under that license.
+
+  See the COPYING file for full details on the licensing of Subunit.
+
+  subunit reuses iso8601 by Michael Twomey, distributed under an MIT style
+  licence - see python/iso8601/LICENSE for details.
+
+Subunit
+-------
+
+Subunit is a streaming protocol for test results. The protocol is human
+readable and easily generated and parsed. By design all the components of 
+the protocol conceptually fit into the xUnit TestCase->TestResult interaction.
+
+Subunit comes with command line filters to process a subunit stream and
+language bindings for python, C, C++ and shell. Bindings are easy to write
+for other languages.
+
+A number of useful things can be done easily with subunit:
+ * Test aggregation: Tests run separately can be combined and then
+   reported/displayed together. For instance, tests from different languages
+   can be shown as a seamless whole.
+ * Test archiving: A test run may be recorded and replayed later.
+ * Test isolation: Tests that may crash or otherwise interact badly with each
+   other can be run seperately and then aggregated, rather than interfering
+   with each other.
+ * Grid testing: subunit can act as the necessary serialisation and
+   deserialiation to get test runs on distributed machines to be reported in
+   real time.
+
+Subunit supplies the following filters:
+ * tap2subunit - convert perl's TestAnythingProtocol to subunit.
+ * subunit2pyunit - convert a subunit stream to pyunit test results.
+ * subunit2gtk - show a subunit stream in GTK.
+ * subunit2junitxml - convert a subunit stream to JUnit's XML format.
+ * subunit-diff - compare two subunit streams.
+ * subunit-filter - filter out tests from a subunit stream.
+ * subunit-ls - list info about tests present in a subunit stream.
+ * subunit-stats - generate a summary of a subunit stream.
+ * subunit-tags - add or remove tags from a stream.
+
+Integration with other tools
+----------------------------
+
+Subunit's language bindings act as integration with various test runners like
+'check', 'cppunit', Python's 'unittest'. Beyond that a small amount of glue
+(typically a few lines) will allow Subunit to be used in more sophisticated
+ways.
+
+Python
+======
+
+Subunit has excellent Python support: most of the filters and tools are written
+in python and there are facilities for using Subunit to increase test isolation
+seamlessly within a test suite.
+
+One simple way to run an existing python test suite and have it output subunit
+is the module ``subunit.run``::
+
+  $ python -m subunit.run mypackage.tests.test_suite
+For more information on the Python support Subunit offers , please see
+``pydoc subunit``, or the source in ``python/subunit/__init__.py``
+
+C
+=
+
+Subunit has C bindings to emit the protocol, and comes with a patch for 'check'
+which has been nominally accepted by the 'check' developers. See 'c/README' for
+more details.
+
+C++
+===
+
+The C library is includable and usable directly from C++. A TestListener for
+CPPUnit is included in the Subunit distribution. See 'c++/README' for details.
+
+shell
+=====
+
+Similar to C, the shell bindings consist of simple functions to output protocol
+elements, and a patch for adding subunit output to the 'ShUnit' shell test
+runner. See 'shell/README' for details.
+
+Filter recipes
+--------------
+
+To ignore some failing tests whose root cause is already known::
+
+  subunit-filter --without 'AttributeError.*flavor'
+
+
+The protocol
+------------
+
+Sample subunit wire contents
+----------------------------
+
+The following::
+  test: test foo works
+  success: test foo works.
+  test: tar a file.
+  failure: tar a file. [
+  ..
+   ]..  space is eaten.
+  foo.c:34 WARNING foo is not defined.
+  ]
+  a writeln to stdout
+
+When run through subunit2pyunit::
+  .F
+  a writeln to stdout
+
+  ========================
+  FAILURE: tar a file.
+  -------------------
+  ..
+  ]..  space is eaten.
+  foo.c:34 WARNING foo is not defined.
+
+
+Subunit protocol description
+============================
+
+This description is being ported to an EBNF style. Currently its only partly in
+that style, but should be fairly clear all the same. When in doubt, refer the
+source (and ideally help fix up the description!). Generally the protocol is
+line orientated and consists of either directives and their parameters, or
+when outside a DETAILS region unexpected lines which are not interpreted by
+the parser - they should be forwarded unaltered.
+
+test|testing|test:|testing: test label
+success|success:|successful|successful: test label
+success|success:|successful|successful: test label DETAILS
+failure: test label
+failure: test label DETAILS
+error: test label
+error: test label DETAILS
+skip[:] test label
+skip[:] test label DETAILS
+xfail[:] test label
+xfail[:] test label DETAILS
+progress: [+|-]X
+progress: push
+progress: pop
+tags: [-]TAG ...
+time: YYYY-MM-DD HH:MM:SSZ
+
+DETAILS ::= BRACKETED | MULTIPART
+BRACKETED ::= '[' CR lines ']' CR
+MULTIPART ::= '[ multipart' CR PART* ']' CR
+PART ::= PART_TYPE CR NAME CR PART_BYTES CR
+PART_TYPE ::= Content-Type: type/sub-type(;parameter=value,parameter=value)
+PART_BYTES ::= (DIGITS CR LF BYTE{DIGITS})* '0' CR LF
+
+unexpected output on stdout -> stdout.
+exit w/0 or last test completing -> error
+
+Tags given outside a test are applied to all following tests
+Tags given after a test: line and before the result line for the same test
+apply only to that test, and inherit the current global tags.
+A '-' before a tag is used to remove tags - e.g. to prevent a global tag
+applying to a single test, or to cancel a global tag.
+
+The progress directive is used to provide progress information about a stream
+so that stream consumer can provide completion estimates, progress bars and so
+on. Stream generators that know how many tests will be present in the stream
+should output "progress: COUNT". Stream filters that add tests should output
+"progress: +COUNT", and those that remove tests should output
+"progress: -COUNT". An absolute count should reset the progress indicators in
+use - it indicates that two separate streams from different generators have
+been trivially concatenated together, and there is no knowledge of how many
+more complete streams are incoming. Smart concatenation could scan each stream
+for their count and sum them, or alternatively translate absolute counts into
+relative counts inline. It is recommended that outputters avoid absolute counts
+unless necessary. The push and pop directives are used to provide local regions
+for progress reporting. This fits with hierarchically operating test
+environments - such as those that organise tests into suites - the top-most
+runner can report on the number of suites, and each suite surround its output
+with a (push, pop) pair. Interpreters should interpret a pop as also advancing
+the progress of the restored level by one step. Encountering progress
+directives between the start and end of a test pair indicates that a previous
+test was interrupted and did not cleanly terminate: it should be implicitly
+closed with an error (the same as when a stream ends with no closing test
+directive for the most recently started test).
+
+The time directive acts as a clock event - it sets the time for all future
+events. The value should be a valid ISO8601 time.
+
+The skip result is used to indicate a test that was found by the runner but not
+fully executed due to some policy or dependency issue. This is represented in
+python using the addSkip interface that testtools
+(https://edge.launchpad.net/testtools) defines. When communicating with a non
+skip aware test result, the test is reported as an error.
+The xfail result is used to indicate a test that was expected to fail failing
+in the expected manner. As this is a normal condition for such tests it is
+represented as a successful test in Python.
+In future, skip and xfail results will be represented semantically in Python,
+but some discussion is underway on the right way to do this.
diff --git a/lib/subunit/c++/README b/lib/subunit/c++/README
new file mode 100644 (file)
index 0000000..7b81844
--- /dev/null
@@ -0,0 +1,50 @@
+#
+#  subunit C++ bindings.
+#  Copyright (C) 2006  Robert Collins <robertc@robertcollins.net>
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+Currently there are no native C++ bindings for subunit. However the C library
+can be used from C++ safely. A CPPUnit listener is built as part of Subunit to
+allow CPPUnit users to simply get Subunit output.
+
+To use the listener, use pkg-config (or your preferred replacement) to get the
+cflags and link settings from libcppunit_subunit.pc.
+
+In your test driver main, use SubunitTestProgressListener, as shown in this
+example main::
+
+  {
+    // Create the event manager and test controller
+    CPPUNIT_NS::TestResult controller;
+  
+    // Add a listener that collects test result
+    // so we can get the overall status.
+    // note this isn't needed for subunit...
+    CPPUNIT_NS::TestResultCollector result;
+    controller.addListener( &result );
+  
+    // Add a listener that print test activity in subunit format.
+    CPPUNIT_NS::SubunitTestProgressListener progress;
+    controller.addListener( &progress );
+  
+    // Add the top suite to the test runner
+    CPPUNIT_NS::TestRunner runner;
+    runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() );
+    runner.run( controller );
+  
+    return result.wasSuccessful() ? 0 : 1;
+  }
diff --git a/lib/subunit/c++/SubunitTestProgressListener.cpp b/lib/subunit/c++/SubunitTestProgressListener.cpp
new file mode 100644 (file)
index 0000000..76cd9e1
--- /dev/null
@@ -0,0 +1,63 @@
+/*  Subunit test listener for cppunit (http://cppunit.sourceforge.net).
+ *  Copyright (C) 2006  Robert Collins <robertc@robertcollins.net>
+ *
+ *  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+ *  license at the users choice. A copy of both licenses are available in the
+ *  project source as Apache-2.0 and BSD. You may not use this file except in
+ *  compliance with one of these two licences.
+ *  
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under these licenses is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the license you chose for the specific language governing permissions
+ *  and limitations under that license.
+ */
+
+#include <cppunit/Exception.h>
+#include <cppunit/Test.h>
+#include <cppunit/TestFailure.h>
+#include <cppunit/TextOutputter.h>
+#include <iostream>
+
+// Have to be able to import the public interface without config.h.
+#include "SubunitTestProgressListener.h"
+#include "config.h"
+#include "subunit/child.h"
+
+
+CPPUNIT_NS_BEGIN
+
+
+void 
+SubunitTestProgressListener::startTest( Test *test )
+{
+  subunit_test_start(test->getName().c_str());
+  last_test_failed = false;
+}
+
+void 
+SubunitTestProgressListener::addFailure( const TestFailure &failure )
+{
+  std::ostringstream capture_stream;
+  TextOutputter outputter(NULL, capture_stream);
+  outputter.printFailureLocation(failure.sourceLine());
+  outputter.printFailureDetail(failure.thrownException());
+
+  if (failure.isError())
+      subunit_test_error(failure.failedTestName().c_str(),
+                        capture_stream.str().c_str());
+  else
+      subunit_test_fail(failure.failedTestName().c_str(),
+                        capture_stream.str().c_str());
+  last_test_failed = true;
+}
+
+void 
+SubunitTestProgressListener::endTest( Test *test)
+{
+  if (!last_test_failed)
+      subunit_test_pass(test->getName().c_str());
+}
+
+
+CPPUNIT_NS_END
diff --git a/lib/subunit/c++/SubunitTestProgressListener.h b/lib/subunit/c++/SubunitTestProgressListener.h
new file mode 100644 (file)
index 0000000..5206d83
--- /dev/null
@@ -0,0 +1,56 @@
+/*  Subunit test listener for cppunit (http://cppunit.sourceforge.net).
+ *  Copyright (C) 2006  Robert Collins <robertc@robertcollins.net>
+ *
+ *  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+ *  license at the users choice. A copy of both licenses are available in the
+ *  project source as Apache-2.0 and BSD. You may not use this file except in
+ *  compliance with one of these two licences.
+ *  
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under these licenses is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the license you chose for the specific language governing permissions
+ *  and limitations under that license.
+ */
+#ifndef CPPUNIT_SUBUNITTESTPROGRESSLISTENER_H
+#define CPPUNIT_SUBUNITTESTPROGRESSLISTENER_H
+
+#include <cppunit/TestListener.h>
+
+
+CPPUNIT_NS_BEGIN
+
+
+/*! 
+ * \brief TestListener that outputs subunit
+ * (http://www.robertcollins.net/unittest/subunit) compatible output.
+ * \ingroup TrackingTestExecution
+ */
+class CPPUNIT_API SubunitTestProgressListener : public TestListener
+{
+public:
+  SubunitTestProgressListener() {}
+  
+  void startTest( Test *test );
+
+  void addFailure( const TestFailure &failure );
+
+  void endTest( Test *test );
+
+private:
+  /// Prevents the use of the copy constructor.
+  SubunitTestProgressListener( const SubunitTestProgressListener &copy );
+
+  /// Prevents the use of the copy operator.
+  void operator =( const SubunitTestProgressListener &copy );
+
+private:
+  int last_test_failed;
+};
+
+
+CPPUNIT_NS_END
+
+#endif  // CPPUNIT_SUBUNITTESTPROGRESSLISTENER_H
+
diff --git a/lib/subunit/c/README b/lib/subunit/c/README
new file mode 100644 (file)
index 0000000..b62fd45
--- /dev/null
@@ -0,0 +1,68 @@
+#
+#  subunit C bindings.
+#  Copyright (C) 2006  Robert Collins <robertc@robertcollins.net>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+
+This subtree contains an implementation of the subunit child protocol.
+Currently I have no plans to write a test runner in C, so I have not written
+an implementation of the parent protocol. [but will happily accept patches].
+This implementation is built using SCons and tested via 'check'.
+See the tests/ directory for the test programs.
+You can use `make check` or `scons check` to run the tests. 
+
+The C protocol consists of four functions which you can use to output test
+metadata trivially. See lib/subunit_child.[ch] for details.
+
+However, this is not a test runner - subunit provides no support for [for
+instance] managing assertions, cleaning up on errors etc. You can look at
+'check' (http://check.sourceforge.net/) or
+'gunit' (https://garage.maemo.org/projects/gunit) for C unit test
+frameworks. 
+There is a patch for 'check' (check-subunit-*.patch) in this source tree.
+Its also available as request ID #1470750 in the sourceforge request tracker
+http://sourceforge.net/tracker/index.php. The 'check' developers have indicated
+they will merge this during the current release cycle.
+
+If you are a test environment maintainer - either homegrown, or 'check' or
+'gunit' or some other, you will to know how the subunit calls should be used.
+Here is what a manually written test using the bindings might look like:
+
+
+void
+a_test(void) {
+  int result;
+  subunit_test_start("test name");
+  # determine if test passes or fails
+  result = SOME_VALUE;
+  if (!result) {
+    subunit_test_pass("test name");
+  } else {
+    subunit_test_fail("test name",
+      "Something went wrong running something:\n"
+      "exited with result: '%s'", result);
+  }
+}
+
+Which when run with a subunit test runner will generate something like:
+test name ... ok
+
+on success, and:
+
+test name ... FAIL
+
+======================================================================
+FAIL: test name
+----------------------------------------------------------------------
+RemoteError:
+Something went wrong running something:
+exited with result: '1'
diff --git a/lib/subunit/c/include/subunit/child.h b/lib/subunit/c/include/subunit/child.h
new file mode 100644 (file)
index 0000000..0a4e601
--- /dev/null
@@ -0,0 +1,79 @@
+/**
+ *
+ *  subunit C bindings.
+ *  Copyright (C) 2006  Robert Collins <robertc@robertcollins.net>
+ *
+ *  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+ *  license at the users choice. A copy of both licenses are available in the
+ *  project source as Apache-2.0 and BSD. You may not use this file except in
+ *  compliance with one of these two licences.
+ *  
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under these licenses is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the license you chose for the specific language governing permissions
+ *  and limitations under that license.
+ **/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * subunit_test_start:
+ *
+ * Report that a test is starting.
+ * @name: test case name
+ */
+extern void subunit_test_start(char const * const name);
+
+
+/**
+ * subunit_test_pass:
+ *
+ * Report that a test has passed.
+ *
+ * @name: test case name
+ */
+extern void subunit_test_pass(char const * const name);
+
+
+/**
+ * subunit_test_fail:
+ *
+ * Report that a test has failed.
+ * @name: test case name
+ * @error: a string describing the error.
+ */
+extern void subunit_test_fail(char const * const name, char const * const error);
+
+
+/**
+ * subunit_test_error:
+ *
+ * Report that a test has errored. An error is an unintentional failure - i.e.
+ * a segfault rather than a failed assertion.
+ * @name: test case name
+ * @error: a string describing the error.
+ */
+extern void subunit_test_error(char const * const name,
+                               char const * const error);
+
+
+/**
+ * subunit_test_skip:
+ *
+ * Report that a test has been skipped. An skip is a test that has not run to
+ * conclusion but hasn't given an error either - its result is unknown.
+ * @name: test case name
+ * @reason: a string describing the reason for the skip.
+ */
+extern void subunit_test_skip(char const * const name, 
+                             char const * const reason);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/lib/subunit/c/lib/child.c b/lib/subunit/c/lib/child.c
new file mode 100644 (file)
index 0000000..2b59747
--- /dev/null
@@ -0,0 +1,82 @@
+/**
+ *
+ *  subunit C child-side bindings: report on tests being run.
+ *  Copyright (C) 2006  Robert Collins <robertc@robertcollins.net>
+ *
+ *  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+ *  license at the users choice. A copy of both licenses are available in the
+ *  project source as Apache-2.0 and BSD. You may not use this file except in
+ *  compliance with one of these two licences.
+ *  
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under these licenses is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the license you chose for the specific language governing permissions
+ *  and limitations under that license.
+ **/
+
+#include <stdio.h>
+#include <string.h>
+#include "subunit/child.h"
+
+/* Write details about a test event. It is the callers responsibility to ensure
+ * that details are only provided for events the protocol expects details on.
+ * @event: The event - e.g. 'skip'
+ * @name: The test name/id.
+ * @details: The details of the event, may be NULL if no details are present.
+ */
+static void
+subunit_send_event(char const * const event, char const * const name,
+                  char const * const details)
+{
+  if (NULL == details) {
+    fprintf(stdout, "%s: %s\n", event, name);
+  } else {
+    fprintf(stdout, "%s: %s [\n", event, name);
+    fprintf(stdout, "%s", details);
+    if (details[strlen(details) - 1] != '\n')
+      fprintf(stdout, "\n");
+    fprintf(stdout, "]\n");
+  }
+  fflush(stdout);
+}
+
+/* these functions all flush to ensure that the test runner knows the action
+ * that has been taken even if the subsequent test etc takes a long time or
+ * never completes (i.e. a segfault).
+ */
+
+void
+subunit_test_start(char const * const name)
+{
+  subunit_send_event("test", name, NULL);
+}
+
+
+void
+subunit_test_pass(char const * const name)
+{
+  /* TODO: add success details as an option */
+  subunit_send_event("success", name, NULL);
+}
+
+
+void
+subunit_test_fail(char const * const name, char const * const error)
+{
+  subunit_send_event("failure", name, error);
+}
+
+
+void
+subunit_test_error(char const * const name, char const * const error)
+{
+  subunit_send_event("error", name, error);
+}
+
+
+void
+subunit_test_skip(char const * const name, char const * const reason)
+{
+  subunit_send_event("skip", name, reason);
+}
diff --git a/lib/subunit/c/tests/test_child.c b/lib/subunit/c/tests/test_child.c
new file mode 100644 (file)
index 0000000..6399eeb
--- /dev/null
@@ -0,0 +1,192 @@
+/**
+ *
+ *  subunit C bindings.
+ *  Copyright (C) 2006  Robert Collins <robertc@robertcollins.net>
+ *
+ *  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+ *  license at the users choice. A copy of both licenses are available in the
+ *  project source as Apache-2.0 and BSD. You may not use this file except in
+ *  compliance with one of these two licences.
+ *  
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under these licenses is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the license you chose for the specific language governing permissions
+ *  and limitations under that license.
+ **/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <check.h>
+
+#include "subunit/child.h"
+
+/**
+ * Helper function to capture stdout, run some call, and check what
+ * was written.
+ * @expected the expected stdout content
+ * @function the function to call.
+ **/
+static void
+test_stdout_function(char const * expected,
+                     void (*function)(void))
+{
+    /* test that the start function emits a correct test: line. */
+    int bytecount;
+    int old_stdout;
+    int new_stdout[2];
+    char buffer[100];
+    /* we need a socketpair to capture stdout in */
+    fail_if(pipe(new_stdout), "Failed to create a socketpair.");
+    /* backup stdout so we can replace it */
+    old_stdout = dup(1);
+    if (old_stdout == -1) {
+      close(new_stdout[0]);
+      close(new_stdout[1]);
+      fail("Failed to backup stdout before replacing.");
+    }
+    /* redirect stdout so we can analyse it */
+    if (dup2(new_stdout[1], 1) != 1) {
+      close(old_stdout);
+      close(new_stdout[0]);
+      close(new_stdout[1]);
+      fail("Failed to redirect stdout");
+    }
+    /* yes this can block. Its a test case with < 100 bytes of output.
+     * DEAL.
+     */
+    function();
+    /* restore stdout now */
+    if (dup2(old_stdout, 1) != 1) {
+      close(old_stdout);
+      close(new_stdout[0]);
+      close(new_stdout[1]);
+      fail("Failed to restore stdout");
+    }
+    /* and we dont need the write side any more */
+    if (close(new_stdout[1])) {
+      close(new_stdout[0]);
+      fail("Failed to close write side of socketpair.");
+    }
+    /* get the output */
+    bytecount = read(new_stdout[0], buffer, 100);
+    if (0 > bytecount) {
+      close(new_stdout[0]);
+      fail("Failed to read captured output.");
+    }
+    buffer[bytecount]='\0';
+    /* and we dont need the read side any more */
+    fail_if(close(new_stdout[0]), "Failed to close write side of socketpair.");
+    /* compare with expected outcome */
+    fail_if(strcmp(expected, buffer), "Did not get expected output [%s], got [%s]", expected, buffer);
+}
+
+
+static void
+call_test_start(void)
+{
+    subunit_test_start("test case");
+}
+
+
+START_TEST (test_start)
+{
+    test_stdout_function("test: test case\n", call_test_start);
+}
+END_TEST
+
+
+static void
+call_test_pass(void)
+{
+    subunit_test_pass("test case");
+}
+
+
+START_TEST (test_pass)
+{
+    test_stdout_function("success: test case\n", call_test_pass);
+}
+END_TEST
+
+
+static void
+call_test_fail(void)
+{
+    subunit_test_fail("test case", "Multiple lines\n of error\n");
+}
+
+
+START_TEST (test_fail)
+{
+    test_stdout_function("failure: test case [\n"
+                         "Multiple lines\n"
+                        " of error\n"
+                        "]\n",
+                        call_test_fail);
+}
+END_TEST
+
+
+static void
+call_test_error(void)
+{
+    subunit_test_error("test case", "Multiple lines\n of output\n");
+}
+
+
+START_TEST (test_error)
+{
+    test_stdout_function("error: test case [\n"
+                         "Multiple lines\n"
+                        " of output\n"
+                        "]\n",
+                        call_test_error);
+}
+END_TEST
+
+
+static void
+call_test_skip(void)
+{
+    subunit_test_skip("test case", "Multiple lines\n of output\n");
+}
+
+
+START_TEST (test_skip)
+{
+    test_stdout_function("skip: test case [\n"
+                         "Multiple lines\n"
+                        " of output\n"
+                        "]\n",
+                        call_test_skip);
+}
+END_TEST
+
+static Suite *
+child_suite(void)
+{
+    Suite *s = suite_create("subunit_child");
+    TCase *tc_core = tcase_create("Core");
+    suite_add_tcase (s, tc_core);
+    tcase_add_test (tc_core, test_start);
+    tcase_add_test (tc_core, test_pass);
+    tcase_add_test (tc_core, test_fail);
+    tcase_add_test (tc_core, test_error);
+    tcase_add_test (tc_core, test_skip);
+    return s;
+}
+
+
+int
+main(void)
+{
+  int nf;
+  Suite *s = child_suite();
+  SRunner *sr = srunner_create(s);
+  srunner_run_all(sr, CK_NORMAL);
+  nf = srunner_ntests_failed(sr);
+  srunner_free(sr);
+  return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/lib/subunit/configure.ac b/lib/subunit/configure.ac
new file mode 100644 (file)
index 0000000..496aea5
--- /dev/null
@@ -0,0 +1,75 @@
+m4_define([SUBUNIT_MAJOR_VERSION], [0])
+m4_define([SUBUNIT_MINOR_VERSION], [0])
+m4_define([SUBUNIT_MICRO_VERSION], [5])
+m4_define([SUBUNIT_VERSION],
+m4_defn([SUBUNIT_MAJOR_VERSION]).m4_defn([SUBUNIT_MINOR_VERSION]).m4_defn([SUBUNIT_MICRO_VERSION]))
+AC_PREREQ([2.59])
+AC_INIT([subunit], [SUBUNIT_VERSION], [subunit-dev@lists.launchpad.net])
+AC_CONFIG_SRCDIR([c/lib/child.c])
+AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
+AC_CONFIG_MACRO_DIR([m4])
+[SUBUNIT_MAJOR_VERSION]=SUBUNIT_MAJOR_VERSION
+[SUBUNIT_MINOR_VERSION]=SUBUNIT_MINOR_VERSION
+[SUBUNIT_MICRO_VERSION]=SUBUNIT_MICRO_VERSION
+[SUBUNIT_VERSION]=SUBUNIT_VERSION
+AC_SUBST([SUBUNIT_MAJOR_VERSION])
+AC_SUBST([SUBUNIT_MINOR_VERSION])
+AC_SUBST([SUBUNIT_MICRO_VERSION])
+AC_SUBST([SUBUNIT_VERSION])
+AC_USE_SYSTEM_EXTENSIONS
+AC_PROG_CC
+AC_PROG_CXX
+AM_PROG_CC_C_O
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_LIBTOOL
+AM_PATH_PYTHON
+
+AS_IF([test "$GCC" = "yes"],
+      [
+  SUBUNIT_CFLAGS="-Wall -Werror -Wextra -Wstrict-prototypes "
+  SUBUNIT_CFLAGS="$SUBUNIT_CFLAGS -Wmissing-prototypes -Wwrite-strings "
+  SUBUNIT_CFLAGS="$SUBUNIT_CFLAGS -Wno-variadic-macros "
+  SUBUNIT_CXXFLAGS="-Wall -Werror -Wextra -Wwrite-strings -Wno-variadic-macros"
+      ])
+
+AM_CFLAGS="$SUBUNIT_CFLAGS -I\$(top_srcdir)/c/include"
+AM_CXXFLAGS="$SUBUNIT_CXXFLAGS -I\$(top_srcdir)/c/include"
+AC_SUBST(AM_CFLAGS)
+AC_SUBST(AM_CXXFLAGS)
+
+# Checks for libraries.
+
+# Checks for header files.
+AC_CHECK_HEADERS([stdlib.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+AC_STRUCT_TM
+
+AC_CHECK_SIZEOF(int, 4)
+AC_CHECK_SIZEOF(short, 2)
+AC_CHECK_SIZEOF(long, 4)
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_FUNC_REALLOC
+
+# Easier memory management.
+# C unit testing.
+PKG_CHECK_MODULES([CHECK], [check >= 0.9.4])
+# C++ unit testing.
+PKG_CHECK_MODULES([CPPUNIT], [cppunit])
+
+# Output files
+AC_CONFIG_HEADERS([config.h])
+
+AC_CONFIG_FILES([libsubunit.pc
+                libcppunit_subunit.pc
+                 Makefile
+                perl/Makefile.PL
+                 ])
+AC_OUTPUT
diff --git a/lib/subunit/filters/subunit-filter b/lib/subunit/filters/subunit-filter
new file mode 100755 (executable)
index 0000000..c06a03a
--- /dev/null
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+#  subunit: extensions to python unittest to get test results from subprocesses.
+#  Copyright (C) 2008  Robert Collins <robertc@robertcollins.net>
+#            (C) 2009  Martin Pool
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+#
+
+"""Filter a subunit stream to include/exclude tests.
+
+The default is to strip successful tests.
+
+Tests can be filtered by Python regular expressions with --with and --without,
+which match both the test name and the error text (if any).  The result
+contains tests which match any of the --with expressions and none of the
+--without expressions.  For case-insensitive matching prepend '(?i)'.
+Remember to quote shell metacharacters.
+"""
+
+from optparse import OptionParser
+import sys
+import unittest
+import re
+
+from subunit import (
+    DiscardStream,
+    ProtocolTestCase,
+    TestProtocolClient,
+    )
+from subunit.test_results import TestResultFilter
+
+parser = OptionParser(description=__doc__)
+parser.add_option("--error", action="store_false",
+    help="include errors", default=False, dest="error")
+parser.add_option("-e", "--no-error", action="store_true",
+    help="exclude errors", dest="error")
+parser.add_option("--failure", action="store_false",
+    help="include failures", default=False, dest="failure")
+parser.add_option("-f", "--no-failure", action="store_true",
+    help="include failures", dest="failure")
+parser.add_option("--no-passthrough", action="store_true",
+    help="Hide all non subunit input.", default=False, dest="no_passthrough")
+parser.add_option("-s", "--success", action="store_false",
+    help="include successes", dest="success")
+parser.add_option("--no-skip", action="store_true",
+    help="exclude skips", dest="skip")
+parser.add_option("--no-success", action="store_true",
+    help="exclude successes", default=True, dest="success")
+parser.add_option("-m", "--with", type=str,
+    help="regexp to include (case-sensitive by default)",
+    action="append", dest="with_regexps")
+parser.add_option("--without", type=str,
+    help="regexp to exclude (case-sensitive by default)",
+    action="append", dest="without_regexps")
+
+(options, args) = parser.parse_args()
+
+
+def _compile_re_from_list(l):
+    return re.compile("|".join(l), re.MULTILINE)
+
+
+def _make_regexp_filter(with_regexps, without_regexps):
+    """Make a callback that checks tests against regexps.
+
+    with_regexps and without_regexps are each either a list of regexp strings,
+    or None.
+    """
+    with_re = with_regexps and _compile_re_from_list(with_regexps)
+    without_re = without_regexps and _compile_re_from_list(without_regexps)
+
+    def check_regexps(test, outcome, err, details):
+        """Check if this test and error match the regexp filters."""
+        test_str = str(test) + outcome + str(err) + str(details)
+        if with_re and not with_re.search(test_str):
+            return False
+        if without_re and without_re.search(test_str):
+            return False
+        return True
+    return check_regexps
+
+
+regexp_filter = _make_regexp_filter(options.with_regexps,
+        options.without_regexps)
+result = TestProtocolClient(sys.stdout)
+result = TestResultFilter(result, filter_error=options.error,
+    filter_failure=options.failure, filter_success=options.success,
+    filter_skip=options.skip,
+    filter_predicate=regexp_filter)
+if options.no_passthrough:
+    passthrough_stream = DiscardStream()
+else:
+    passthrough_stream = None
+test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream)
+test.run(result)
+sys.exit(0)
diff --git a/lib/subunit/filters/subunit-ls b/lib/subunit/filters/subunit-ls
new file mode 100755 (executable)
index 0000000..15ec4b0
--- /dev/null
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+#  subunit: extensions to python unittest to get test results from subprocesses.
+#  Copyright (C) 2008  Robert Collins <robertc@robertcollins.net>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+#
+
+"""List tests in a subunit stream."""
+
+from optparse import OptionParser
+import sys
+import unittest
+
+from subunit import DiscardStream, ProtocolTestCase
+
+class TestIdPrintingResult(unittest.TestResult):
+
+    def __init__(self, stream, show_times=False):
+        """Create a FilterResult object outputting to stream."""
+        unittest.TestResult.__init__(self)
+        self._stream = stream
+        self.failed_tests = 0
+        self.__time = 0
+        self.show_times = show_times
+        self._test = None
+        self._test_duration = 0
+        
+    def addError(self, test, err):
+        self.failed_tests += 1
+        self._test = test
+
+    def addFailure(self, test, err):
+        self.failed_tests += 1
+        self._test = test
+
+    def addSuccess(self, test):
+        self._test = test
+
+    def reportTest(self, test, duration):
+        if self.show_times:
+            seconds = duration.seconds
+            seconds += duration.days * 3600 * 24
+            seconds += duration.microseconds / 1000000.0
+            self._stream.write(test.id() + ' %0.3f\n' % seconds)
+        else:
+            self._stream.write(test.id() + '\n')
+
+    def startTest(self, test):
+        self._start_time = self._time()
+
+    def stopTest(self, test):
+        test_duration = self._time() - self._start_time
+        self.reportTest(self._test, test_duration)
+
+    def time(self, time):
+        self.__time = time
+
+    def _time(self):
+        return self.__time
+
+    def wasSuccessful(self):
+        "Tells whether or not this result was a success"
+        return self.failed_tests == 0
+
+
+parser = OptionParser(description=__doc__)
+parser.add_option("--times", action="store_true",
+    help="list the time each test took (requires a timestamped stream)",
+        default=False)
+parser.add_option("--no-passthrough", action="store_true",
+    help="Hide all non subunit input.", default=False, dest="no_passthrough")
+(options, args) = parser.parse_args()
+result = TestIdPrintingResult(sys.stdout, options.times)
+if options.no_passthrough:
+    passthrough_stream = DiscardStream()
+else:
+    passthrough_stream = None
+test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream)
+test.run(result)
+if result.wasSuccessful():
+    exit_code = 0
+else:
+    exit_code = 1
+sys.exit(exit_code)
diff --git a/lib/subunit/filters/subunit-notify b/lib/subunit/filters/subunit-notify
new file mode 100755 (executable)
index 0000000..758e7fc
--- /dev/null
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+#  subunit: extensions to python unittest to get test results from subprocesses.
+#  Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+#
+
+"""Notify the user of a finished test run."""
+
+from optparse import OptionParser
+import sys
+
+import pygtk
+pygtk.require('2.0')
+import pynotify
+
+from subunit import DiscardStream, ProtocolTestCase, TestResultStats
+
+if not pynotify.init("Subunit-notify"):
+    sys.exit(1)
+
+parser = OptionParser(description=__doc__)
+parser.add_option("--no-passthrough", action="store_true",
+    help="Hide all non subunit input.", default=False, dest="no_passthrough")
+parser.add_option("-f", "--forward", action="store_true", default=False,
+    help="Forward subunit stream on stdout.")
+(options, args) = parser.parse_args()
+result = TestResultStats(sys.stdout)
+if options.no_passthrough:
+    passthrough_stream = DiscardStream()
+else:
+    passthrough_stream = None
+if options.forward:
+    forward_stream = sys.stdout
+else:
+    forward_stream = None
+test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream,
+                        forward=forward_stream)
+test.run(result)
+if result.failed_tests > 0:
+    summary = "Test run failed"
+else:
+    summary = "Test run successful"
+body = "Total tests: %d; Passed: %d; Failed: %d" % (
+    result.total_tests,
+    result.passed_tests,
+    result.failed_tests,
+    )
+nw = pynotify.Notification(summary, body)
+nw.show()
+
+if result.wasSuccessful():
+    exit_code = 0
+else:
+    exit_code = 1
+sys.exit(exit_code)
diff --git a/lib/subunit/filters/subunit-stats b/lib/subunit/filters/subunit-stats
new file mode 100755 (executable)
index 0000000..4734988
--- /dev/null
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+#  subunit: extensions to python unittest to get test results from subprocesses.
+#  Copyright (C) 2009  Robert Collins <robertc@robertcollins.net>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+#
+
+"""Filter a subunit stream to get aggregate statistics."""
+
+from optparse import OptionParser
+import sys
+import unittest
+
+from subunit import DiscardStream, ProtocolTestCase, TestResultStats
+
+parser = OptionParser(description=__doc__)
+parser.add_option("--no-passthrough", action="store_true",
+    help="Hide all non subunit input.", default=False, dest="no_passthrough")
+(options, args) = parser.parse_args()
+result = TestResultStats(sys.stdout)
+if options.no_passthrough:
+    passthrough_stream = DiscardStream()
+else:
+    passthrough_stream = None
+test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream)
+test.run(result)
+result.formatStats()
+if result.wasSuccessful():
+    exit_code = 0
+else:
+    exit_code = 1
+sys.exit(exit_code)
diff --git a/lib/subunit/filters/subunit-tags b/lib/subunit/filters/subunit-tags
new file mode 100755 (executable)
index 0000000..edbbfce
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+#  subunit: extensions to python unittest to get test results from subprocesses.
+#  Copyright (C) 2009  Robert Collins <robertc@robertcollins.net>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+#
+
+"""A filter to change tags on a subunit stream.
+
+subunit-tags foo -> adds foo
+subunit-tags foo -bar -> adds foo and removes bar
+"""
+
+import sys
+
+from subunit import tag_stream
+sys.exit(tag_stream(sys.stdin, sys.stdout, sys.argv[1:]))
diff --git a/lib/subunit/filters/subunit2gtk b/lib/subunit/filters/subunit2gtk
new file mode 100755 (executable)
index 0000000..c2cb2de
--- /dev/null
@@ -0,0 +1,259 @@
+#!/usr/bin/env python
+#  subunit: extensions to python unittest to get test results from subprocesses.
+#  Copyright (C) 2009  Robert Collins <robertc@robertcollins.net>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+#
+
+### The GTK progress bar __init__ function is derived from the pygtk tutorial:
+# The PyGTK Tutorial is Copyright (C) 2001-2005 John Finlay.
+# 
+# The GTK Tutorial is Copyright (C) 1997 Ian Main.
+# 
+# Copyright (C) 1998-1999 Tony Gale.
+# 
+# Permission is granted to make and distribute verbatim copies of this manual
+# provided the copyright notice and this permission notice are preserved on all
+# copies.
+# 
+# Permission is granted to copy and distribute modified versions of this
+# document under the conditions for verbatim copying, provided that this
+# copyright notice is included exactly as in the original, and that the entire
+# resulting derived work is distributed under the terms of a permission notice
+# identical to this one.
+# 
+# Permission is granted to copy and distribute translations of this document
+# into another language, under the above conditions for modified versions.
+# 
+# If you are intending to incorporate this document into a published work,
+# please contact the maintainer, and we will make an effort to ensure that you
+# have the most up to date information available.
+# 
+# There is no guarantee that this document lives up to its intended purpose.
+# This is simply provided as a free resource. As such, the authors and
+# maintainers of the information provided within can not make any guarantee
+# that the information is even accurate.
+
+"""Display a subunit stream in a gtk progress window."""
+
+import sys
+import unittest
+
+import pygtk
+pygtk.require('2.0')
+import gtk, gtk.gdk, gobject
+
+from subunit import (
+    PROGRESS_POP,
+    PROGRESS_PUSH,
+    PROGRESS_SET,
+    TestProtocolServer,
+    )
+from subunit.progress_model import  ProgressModel
+
+
+class GTKTestResult(unittest.TestResult):
+
+    def __init__(self):
+        super(GTKTestResult, self).__init__()
+        # Instance variables (in addition to TestResult)
+        self.window = None
+        self.run_label = None
+        self.ok_label = None
+        self.not_ok_label = None
+        self.total_tests = None
+
+        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+        self.window.set_resizable(True)
+
+        self.window.connect("destroy", gtk.main_quit)
+        self.window.set_title("Tests...")
+        self.window.set_border_width(0)
+
+        vbox = gtk.VBox(False, 5)
+        vbox.set_border_width(10)
+        self.window.add(vbox)
+        vbox.show()
+
+        # Create a centering alignment object
+        align = gtk.Alignment(0.5, 0.5, 0, 0)
+        vbox.pack_start(align, False, False, 5)
+        align.show()
+
+        # Create the ProgressBar
+        self.pbar = gtk.ProgressBar()
+        align.add(self.pbar)
+        self.pbar.set_text("Running")
+        self.pbar.show()
+        self.progress_model = ProgressModel()
+
+        separator = gtk.HSeparator()
+        vbox.pack_start(separator, False, False, 0)
+        separator.show()
+
+        # rows, columns, homogeneous
+        table = gtk.Table(2, 3, False)
+        vbox.pack_start(table, False, True, 0)
+        table.show()
+        # Show summary details about the run. Could use an expander.
+        label = gtk.Label("Run:")
+        table.attach(label, 0, 1, 1, 2, gtk.EXPAND | gtk.FILL,
+            gtk.EXPAND | gtk.FILL, 5, 5)
+        label.show()
+        self.run_label = gtk.Label("N/A")
+        table.attach(self.run_label, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL,
+            gtk.EXPAND | gtk.FILL, 5, 5)
+        self.run_label.show()
+
+        label = gtk.Label("OK:")
+        table.attach(label, 0, 1, 2, 3, gtk.EXPAND | gtk.FILL,
+            gtk.EXPAND | gtk.FILL, 5, 5)
+        label.show()
+        self.ok_label = gtk.Label("N/A")
+        table.attach(self.ok_label, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL,
+            gtk.EXPAND | gtk.FILL, 5, 5)
+        self.ok_label.show()
+
+        label = gtk.Label("Not OK:")
+        table.attach(label, 0, 1, 3, 4, gtk.EXPAND | gtk.FILL,
+            gtk.EXPAND | gtk.FILL, 5, 5)
+        label.show()
+        self.not_ok_label = gtk.Label("N/A")
+        table.attach(self.not_ok_label, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL,
+            gtk.EXPAND | gtk.FILL, 5, 5)
+        self.not_ok_label.show()
+
+        self.window.show()
+        # For the demo.
+        self.window.set_keep_above(True)
+        self.window.present()
+
+    def stopTest(self, test):
+        super(GTKTestResult, self).stopTest(test)
+        self.progress_model.advance()
+        if self.progress_model.width() == 0:
+            self.pbar.pulse()
+        else:
+            pos = self.progress_model.pos()
+            width = self.progress_model.width()
+            percentage = (pos / float(width))
+            self.pbar.set_fraction(percentage)
+
+    def stopTestRun(self):
+        try:
+            super(GTKTestResult, self).stopTestRun()
+        except AttributeError:
+            pass
+        self.pbar.set_text('Finished')
+
+    def addError(self, test, err):
+        super(GTKTestResult, self).addError(test, err)
+        self.update_counts()
+
+    def addFailure(self, test, err):
+        super(GTKTestResult, self).addFailure(test, err)
+        self.update_counts()
+
+    def addSuccess(self, test):
+        super(GTKTestResult, self).addSuccess(test)
+        self.update_counts()
+
+    def addSkip(self, test, reason):
+        # addSkip is new in Python 2.7/3.1
+        addSkip = getattr(super(GTKTestResult, self), 'addSkip', None)
+        if callable(addSkip):
+            addSkip(test, reason)
+        self.update_counts()
+
+    def addExpectedFailure(self, test, err):
+        # addExpectedFailure is new in Python 2.7/3.1
+        addExpectedFailure = getattr(super(GTKTestResult, self),
+            'addExpectedFailure', None)
+        if callable(addExpectedFailure):
+            addExpectedFailure(test, err)
+        self.update_counts()
+
+    def addUnexpectedSuccess(self, test):
+        # addUnexpectedSuccess is new in Python 2.7/3.1
+        addUnexpectedSuccess = getattr(super(GTKTestResult, self),
+            'addUnexpectedSuccess', None)
+        if callable(addUnexpectedSuccess):
+            addUnexpectedSuccess(test)
+        self.update_counts()
+
+    def progress(self, offset, whence):
+        if whence == PROGRESS_PUSH:
+            self.progress_model.push()
+        elif whence == PROGRESS_POP:
+            self.progress_model.pop()
+        elif whence == PROGRESS_SET:
+            self.total_tests = offset
+            self.progress_model.set_width(offset)
+        else:
+            self.total_tests += offset
+            self.progress_model.adjust_width(offset)
+
+    def time(self, a_datetime):
+        # We don't try to estimate completion yet.
+        pass
+
+    def update_counts(self):
+        self.run_label.set_text(str(self.testsRun))
+        bad = len(self.failures + self.errors)
+        self.ok_label.set_text(str(self.testsRun - bad))
+        self.not_ok_label.set_text(str(bad))
+
+
+class GIOProtocolTestCase(object):
+
+    def __init__(self, stream, result, on_finish):
+        self.stream = stream
+        self.schedule_read()
+        self.hup_id = gobject.io_add_watch(stream, gobject.IO_HUP, self.hup)
+        self.protocol = TestProtocolServer(result)
+        self.on_finish = on_finish
+
+    def read(self, source, condition, all=False):
+        #NB: \o/ actually blocks
+        line = source.readline()
+        if not line:
+            self.protocol.lostConnection()
+            self.on_finish()
+            return False
+        self.protocol.lineReceived(line)
+        # schedule more IO shortly - if we say we're willing to do it
+        # immediately we starve things.
+        if not all:
+            source_id = gobject.timeout_add(1, self.schedule_read)
+            return False
+        else:
+            return True
+
+    def schedule_read(self):
+        self.read_id = gobject.io_add_watch(self.stream, gobject.IO_IN, self.read)
+
+    def hup(self, source, condition):
+        while self.read(source, condition, all=True): pass
+        self.protocol.lostConnection()
+        gobject.source_remove(self.read_id)
+        self.on_finish()
+        return False
+
+
+result = GTKTestResult()
+test = GIOProtocolTestCase(sys.stdin, result, result.stopTestRun)
+gtk.main()
+if result.wasSuccessful():
+    exit_code = 0
+else:
+    exit_code = 1
+sys.exit(exit_code)
diff --git a/lib/subunit/filters/subunit2junitxml b/lib/subunit/filters/subunit2junitxml
new file mode 100755 (executable)
index 0000000..bea795d
--- /dev/null
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+#  subunit: extensions to python unittest to get test results from subprocesses.
+#  Copyright (C) 2009  Robert Collins <robertc@robertcollins.net>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+#
+
+"""Filter a subunit stream to get aggregate statistics."""
+
+from optparse import OptionParser
+import sys
+import unittest
+
+from subunit import DiscardStream, ProtocolTestCase
+try:
+    from junitxml import JUnitXmlResult
+except ImportError:
+    sys.stderr.write("python-junitxml (https://launchpad.net/pyjunitxml or "
+        "http://pypi.python.org/pypi/junitxml) is required for this filter.")
+    raise
+
+parser = OptionParser(description=__doc__)
+parser.add_option("--no-passthrough", action="store_true",
+    help="Hide all non subunit input.", default=False, dest="no_passthrough")
+parser.add_option("-o", "--output-to",
+    help="Output the XML to this path rather than stdout.")
+parser.add_option("-f", "--forward", action="store_true", default=False,
+    help="Forward subunit stream on stdout.")
+(options, args) = parser.parse_args()
+if options.output_to is None:
+    output_to = sys.stdout
+else:
+    output_to = file(options.output_to, 'wb')
+try:
+    result = JUnitXmlResult(output_to)
+    if options.no_passthrough:
+        passthrough_stream = DiscardStream()
+    else:
+        passthrough_stream = None
+    if options.forward:
+        forward_stream = sys.stdout
+    else:
+        forward_stream = None
+    test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream,
+       forward=forward_stream)
+    result.startTestRun()
+    test.run(result)
+    result.stopTestRun()
+finally:
+    if options.output_to is not None:
+        output_to.close()
+if result.wasSuccessful():
+    exit_code = 0
+else:
+    exit_code = 1
+sys.exit(exit_code)
diff --git a/lib/subunit/filters/subunit2pyunit b/lib/subunit/filters/subunit2pyunit
new file mode 100755 (executable)
index 0000000..83a23d1
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#  subunit: extensions to python unittest to get test results from subprocesses.
+#  Copyright (C) 2009  Robert Collins <robertc@robertcollins.net>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+#
+
+"""Display a subunit stream through python's unittest test runner."""
+
+from optparse import OptionParser
+import sys
+import unittest
+
+from subunit import DiscardStream, ProtocolTestCase, TestProtocolServer
+
+parser = OptionParser(description=__doc__)
+parser.add_option("--no-passthrough", action="store_true",
+    help="Hide all non subunit input.", default=False, dest="no_passthrough")
+parser.add_option("--progress", action="store_true",
+    help="Use bzrlib's test reporter (requires bzrlib)",
+        default=False)
+(options, args) = parser.parse_args()
+if options.no_passthrough:
+    passthrough_stream = DiscardStream()
+else:
+    passthrough_stream = None
+test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream)
+if options.progress:
+    from bzrlib.tests import TextTestRunner
+    from bzrlib import ui
+    ui.ui_factory = ui.make_ui_for_terminal(None, sys.stdout, sys.stderr)
+    runner = TextTestRunner()
+else:
+    runner = unittest.TextTestRunner(verbosity=2)
+if runner.run(test).wasSuccessful():
+    exit_code = 0
+else:
+    exit_code = 1
+sys.exit(exit_code)
diff --git a/lib/subunit/libcppunit_subunit.pc.in b/lib/subunit/libcppunit_subunit.pc.in
new file mode 100644 (file)
index 0000000..98982c7
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: cppunit subunit listener
+Description: Subunit output listener for the CPPUnit test library.
+URL: http://launchpad.net/subunit
+Version: @VERSION@
+Libs: -L${libdir} -lsubunit
+Cflags: -I${includedir}
diff --git a/lib/subunit/libsubunit.pc.in b/lib/subunit/libsubunit.pc.in
new file mode 100644 (file)
index 0000000..6756414
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: subunit
+Description: Subunit test protocol library.
+URL: http://launchpad.net/subunit
+Version: @VERSION@
+Libs: -L${libdir} -lsubunit
+Cflags: -I${includedir}
diff --git a/lib/subunit/perl/Makefile.PL.in b/lib/subunit/perl/Makefile.PL.in
new file mode 100755 (executable)
index 0000000..26e1c18
--- /dev/null
@@ -0,0 +1,20 @@
+use ExtUtils::MakeMaker;
+WriteMakefile(
+    'INSTALL_BASE' => '@prefix@',
+    'NAME'     => 'Subunit',
+    'VERSION' => '@SUBUNIT_VERSION@',
+    'test' => { 'TESTS' => 'tests/*.pl' },
+    'PMLIBDIRS' => [ 'lib' ],
+    'EXE_FILES' => [ '@abs_srcdir@/subunit-diff' ],
+);
+sub MY::postamble {
+<<'EOT';
+check: # test
+
+uninstall_distcheck:
+       rm -fr $(DESTINSTALLARCHLIB)
+
+VPATH = @srcdir@
+.PHONY: uninstall_distcheck
+EOT
+}
diff --git a/lib/subunit/perl/lib/Subunit.pm b/lib/subunit/perl/lib/Subunit.pm
new file mode 100644 (file)
index 0000000..0520674
--- /dev/null
@@ -0,0 +1,162 @@
+# Perl module for parsing and generating the Subunit protocol
+# Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+
+package Subunit;
+use POSIX;
+
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(parse_results $VERSION);
+
+use vars qw ( $VERSION );
+
+$VERSION = '0.0.2';
+
+use strict;
+
+sub parse_results($$$)
+{
+       my ($msg_ops, $statistics, $fh) = @_;
+       my $expected_fail = 0;
+       my $unexpected_fail = 0;
+       my $unexpected_err = 0;
+       my $open_tests = [];
+
+       while(<$fh>) {
+               if (/^test: (.+)\n/) {
+                       $msg_ops->control_msg($_);
+                       $msg_ops->start_test($1);
+                       push (@$open_tests, $1);
+               } elsif (/^time: (\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)Z\n/) {
+                       $msg_ops->report_time(mktime($6, $5, $4, $3, $2, $1-1900));
+               } elsif (/^(success|successful|failure|fail|skip|knownfail|error|xfail): (.*?)( \[)?([ \t]*)\n/) {
+                       $msg_ops->control_msg($_);
+                       my $result = $1;
+                       my $testname = $2;
+                       my $reason = undef;
+                       if ($3) {
+                               $reason = "";
+                               # reason may be specified in next lines
+                               my $terminated = 0;
+                               while(<$fh>) {
+                                       $msg_ops->control_msg($_);
+                                       if ($_ eq "]\n") { $terminated = 1; last; } else { $reason .= $_; }
+                               }
+                               
+                               unless ($terminated) {
+                                       $statistics->{TESTS_ERROR}++;
+                                       $msg_ops->end_test($testname, "error", 1, "reason ($result) interrupted");
+                                       return 1;
+                               }
+                       }
+                       if ($result eq "success" or $result eq "successful") {
+                               pop(@$open_tests); #FIXME: Check that popped value == $testname 
+                               $statistics->{TESTS_EXPECTED_OK}++;
+                               $msg_ops->end_test($testname, $result, 0, $reason);
+                       } elsif ($result eq "xfail" or $result eq "knownfail") {
+                               pop(@$open_tests); #FIXME: Check that popped value == $testname
+                               $statistics->{TESTS_EXPECTED_FAIL}++;
+                               $msg_ops->end_test($testname, $result, 0, $reason);
+                               $expected_fail++;
+                       } elsif ($result eq "failure" or $result eq "fail") {
+                               pop(@$open_tests); #FIXME: Check that popped value == $testname
+                               $statistics->{TESTS_UNEXPECTED_FAIL}++;
+                               $msg_ops->end_test($testname, $result, 1, $reason);
+                               $unexpected_fail++;
+                       } elsif ($result eq "skip") {
+                               $statistics->{TESTS_SKIP}++;
+                               my $last = pop(@$open_tests);
+                               if (defined($last) and $last ne $testname) {
+                                       push (@$open_tests, $testname);
+                               }
+                               $msg_ops->end_test($testname, $result, 0, $reason);
+                       } elsif ($result eq "error") {
+                               $statistics->{TESTS_ERROR}++;
+                               pop(@$open_tests); #FIXME: Check that popped value == $testname
+                               $msg_ops->end_test($testname, $result, 1, $reason);
+                               $unexpected_err++;
+                       } 
+               } else {
+                       $msg_ops->output_msg($_);
+               }
+       }
+
+       while ($#$open_tests+1 > 0) {
+               $msg_ops->end_test(pop(@$open_tests), "error", 1,
+                                  "was started but never finished!");
+               $statistics->{TESTS_ERROR}++;
+               $unexpected_err++;
+       }
+
+       return 1 if $unexpected_err > 0;
+       return 1 if $unexpected_fail > 0;
+       return 0;
+}
+
+sub start_test($)
+{
+       my ($testname) = @_;
+       print "test: $testname\n";
+}
+
+sub end_test($$;$)
+{
+       my $name = shift;
+       my $result = shift;
+       my $reason = shift;
+       if ($reason) {
+               print "$result: $name [\n";
+               print "$reason";
+               print "]\n";
+       } else {
+               print "$result: $name\n";
+       }
+}
+
+sub skip_test($;$)
+{
+       my $name = shift;
+       my $reason = shift;
+       end_test($name, "skip", $reason);
+}
+
+sub fail_test($;$)
+{
+       my $name = shift;
+       my $reason = shift;
+       end_test($name, "fail", $reason);
+}
+
+sub success_test($;$)
+{
+       my $name = shift;
+       my $reason = shift;
+       end_test($name, "success", $reason);
+}
+
+sub xfail_test($;$)
+{
+       my $name = shift;
+       my $reason = shift;
+       end_test($name, "xfail", $reason);
+}
+
+sub report_time($)
+{
+       my ($time) = @_;
+       my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($time);
+       printf "time: %04d-%02d-%02d %02d:%02d:%02dZ\n", $year+1900, $mon, $mday, $hour, $min, $sec;
+}
+
+1;
diff --git a/lib/subunit/perl/lib/Subunit/Diff.pm b/lib/subunit/perl/lib/Subunit/Diff.pm
new file mode 100644 (file)
index 0000000..e7841c3
--- /dev/null
@@ -0,0 +1,85 @@
+#!/usr/bin/perl
+# Diff two subunit streams
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+
+package Subunit::Diff;
+
+use strict;
+
+use Subunit qw(parse_results);
+
+sub control_msg() { }
+sub report_time($$) { }
+
+sub output_msg($$)
+{
+       my ($self, $msg) = @_;
+
+       # No output for now, perhaps later diff this as well ?
+}
+
+sub start_test($$)
+{
+       my ($self, $testname) = @_;
+}
+
+sub end_test($$$$$)
+{
+       my ($self, $testname, $result, $unexpected, $reason) = @_;
+
+       $self->{$testname} = $result;
+}
+
+sub new {
+       my ($class) = @_;
+
+       my $self = { 
+       };
+       bless($self, $class);
+}
+
+sub from_file($)
+{
+       my ($path) = @_;
+       my $statistics = {
+               TESTS_UNEXPECTED_OK => 0,
+               TESTS_EXPECTED_OK => 0,
+               TESTS_UNEXPECTED_FAIL => 0,
+               TESTS_EXPECTED_FAIL => 0,
+               TESTS_ERROR => 0,
+               TESTS_SKIP => 0,
+       };
+
+       my $ret = new Subunit::Diff();
+       open(IN, $path) or return;
+       parse_results($ret, $statistics, *IN);
+       close(IN);
+       return $ret;
+}
+
+sub diff($$)
+{
+       my ($old, $new) = @_;
+       my $ret = {};
+
+       foreach my $testname (keys %$old) {
+               if ($new->{$testname} ne $old->{$testname}) {
+                       $ret->{$testname} = [$old->{$testname}, $new->{$testname}];
+               }
+       }
+
+       return $ret;
+}
+
+1;
diff --git a/lib/subunit/perl/subunit-diff b/lib/subunit/perl/subunit-diff
new file mode 100755 (executable)
index 0000000..581e832
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+# Diff two subunit streams
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+
+use Getopt::Long;
+use strict;
+use FindBin qw($RealBin $Script);
+use lib "$RealBin/lib";
+use Subunit::Diff;
+
+my $old = Subunit::Diff::from_file($ARGV[0]);
+my $new = Subunit::Diff::from_file($ARGV[1]);
+
+my $ret = Subunit::Diff::diff($old, $new);
+
+foreach my $e (sort(keys %$ret)) {
+       printf "%s: %s -> %s\n", $e, $ret->{$e}[0], $ret->{$e}[1];
+}
+
+0;
diff --git a/lib/subunit/runtests.py b/lib/subunit/runtests.py
new file mode 100755 (executable)
index 0000000..8ecc6cd
--- /dev/null
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+# -*- Mode: python -*-
+#
+# Copyright (C) 2004 Canonical.com
+#       Author:      Robert Collins <robert.collins@canonical.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+import unittest
+from subunit.tests.TestUtil import TestVisitor, TestSuite
+import subunit
+import sys
+import os
+import shutil
+import logging
+
+class ParameterisableTextTestRunner(unittest.TextTestRunner):
+    """I am a TextTestRunner whose result class is
+    parameterisable without further subclassing"""
+    def __init__(self, **args):
+        unittest.TextTestRunner.__init__(self, **args)
+        self._resultFactory=None
+    def resultFactory(self, *args):
+        """set or retrieve the result factory"""
+        if args:
+            self._resultFactory=args[0]
+            return self
+        if self._resultFactory is None:
+            self._resultFactory=unittest._TextTestResult
+        return self._resultFactory
+
+    def _makeResult(self):
+        return self.resultFactory()(self.stream, self.descriptions, self.verbosity)
+
+
+class EarlyStoppingTextTestResult(unittest._TextTestResult):
+    """I am a TextTestResult that can optionally stop at the first failure
+    or error"""
+
+    def addError(self, test, err):
+        unittest._TextTestResult.addError(self, test, err)
+        if self.stopOnError():
+            self.stop()
+
+    def addFailure(self, test, err):
+        unittest._TextTestResult.addError(self, test, err)
+        if self.stopOnFailure():
+            self.stop()
+
+    def stopOnError(self, *args):
+        """should this result indicate an abort when an error occurs?
+        TODO parameterise this"""
+        return True
+
+    def stopOnFailure(self, *args):
+        """should this result indicate an abort when a failure error occurs?
+        TODO parameterise this"""
+        return True
+
+
+def earlyStopFactory(*args, **kwargs):
+    """return a an early stopping text test result"""
+    result=EarlyStoppingTextTestResult(*args, **kwargs)
+    return result
+
+
+class ShellTests(subunit.ExecTestCase):
+
+    def test_sourcing(self):
+        """./shell/tests/test_source_library.sh"""
+
+    def test_functions(self):
+        """./shell/tests/test_function_output.sh"""
+
+
+def test_suite():
+    result = TestSuite()
+    result.addTest(subunit.test_suite())
+    result.addTest(ShellTests('test_sourcing'))
+    result.addTest(ShellTests('test_functions'))
+    return result
+
+
+class filteringVisitor(TestVisitor):
+    """I accrue all the testCases I visit that pass a regexp filter on id
+    into my suite
+    """
+
+    def __init__(self, filter):
+        import re
+        TestVisitor.__init__(self)
+        self._suite=None
+        self.filter=re.compile(filter)
+
+    def suite(self):
+        """answer the suite we are building"""
+        if self._suite is None:
+            self._suite=TestSuite()
+        return self._suite
+
+    def visitCase(self, aCase):
+        if self.filter.match(aCase.id()):
+            self.suite().addTest(aCase)
+
+
+def main(argv):
+    """To parameterise what tests are run, run this script like so:
+    python test_all.py REGEX
+    i.e.
+    python test_all.py .*Protocol.*
+    to run all tests with Protocol in their id."""
+    if len(argv) > 1:
+        pattern = argv[1]
+    else:
+        pattern = ".*"
+    visitor = filteringVisitor(pattern)
+    test_suite().visit(visitor)
+    runner = ParameterisableTextTestRunner(verbosity=2)
+    runner.resultFactory(unittest._TextTestResult)
+    if not runner.run(visitor.suite()).wasSuccessful():
+        return 1
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))
diff --git a/lib/subunit/shell/README b/lib/subunit/shell/README
new file mode 100644 (file)
index 0000000..af894a2
--- /dev/null
@@ -0,0 +1,62 @@
+#
+#  subunit shell bindings.
+#  Copyright (C) 2006  Robert Collins <robertc@robertcollins.net>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+#
+
+This tree contains shell bindings to the subunit protocol. They are written
+entirely in shell, and unit tested in shell. See the tests/ directory for the
+test scripts. You can use `make check` to run the tests. There is a trivial
+python test_shell.py which uses the pyunit gui to expose the test results in a
+compact form.
+
+The shell bindings consist of four functions which you can use to output test
+metadata trivially. See share/subunit.sh for the functions and comments.
+
+However, this is not a full test environment, its support code for reporting to
+subunit. You can look at ShUnit (http://shunit.sourceforge.net) for 'proper'
+shell based xUnit functionality. There is a patch for ShUnit 1.3
+(subunit-ui.patch) in the subunit source tree. I hope to have that integrated
+upstream in the near future. I will delete the copy of the patch in the subunit
+tree a release or two later.
+
+If you are a test environment maintainer - either homegrown, or ShUnit or some
+such, you will need to see how the subunit calls should be used. Here is what
+a manually written test using the bindings might look like:
+
+
+subunit_start_test "test name"
+# determine if test passes or fails
+result=$(something)
+if [ $result == 0 ]; then
+  subunit_pass_test "test name"
+else
+  subunit_fail_test "test name" <<END
+Something went wrong running something:
+exited with result: '$func_status'
+END
+fi
+
+Which when run with a subunit test runner will generate something like:
+test name ... ok
+
+on success, and:
+
+test name ... FAIL
+
+======================================================================
+FAIL: test name
+----------------------------------------------------------------------
+RemoteError:
+Something went wrong running something:
+exited with result: '1'
diff --git a/lib/subunit/shell/share/subunit.sh b/lib/subunit/shell/share/subunit.sh
new file mode 100644 (file)
index 0000000..8273727
--- /dev/null
@@ -0,0 +1,56 @@
+#
+#  subunit.sh: shell functions to report test status via the subunit protocol.
+#  Copyright (C) 2006  Robert Collins <robertc@robertcollins.net>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+#
+
+subunit_start_test () {
+  # emit the current protocol start-marker for test $1
+  echo "test: $1"
+}
+
+
+subunit_pass_test () {
+  # emit the current protocol test passed marker for test $1
+  echo "success: $1"
+}
+
+
+subunit_fail_test () {
+  # emit the current protocol fail-marker for test $1, and emit stdin as
+  # the error text.
+  # we use stdin because the failure message can be arbitrarily long, and this
+  # makes it convenient to write in scripts (using <<END syntax.
+  echo "failure: $1 ["
+  cat -
+  echo "]"
+}
+
+
+subunit_error_test () {
+  # emit the current protocol error-marker for test $1, and emit stdin as
+  # the error text.
+  # we use stdin because the failure message can be arbitrarily long, and this
+  # makes it convenient to write in scripts (using <<END syntax.
+  echo "error: $1 ["
+  cat -
+  echo "]"
+}
+
+
+subunit_skip_test () {
+  # emit the current protocol test skipped marker for test $1
+  echo "skip: $1"
+}
+
+
diff --git a/lib/subunit/shell/tests/test_function_output.sh b/lib/subunit/shell/tests/test_function_output.sh
new file mode 100755 (executable)
index 0000000..b78eee6
--- /dev/null
@@ -0,0 +1,97 @@
+#!/bin/bash
+#  subunit shell bindings.
+#  Copyright (C) 2006  Robert Collins <robertc@robertcollins.net>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+#
+
+
+# this script tests the output of the methods. As each is tested we start using
+# it.
+# So the first test manually implements the entire protocol, the next uses the
+# start method and so on.
+# it is assumed that we are running from the 'shell' tree root in the source
+# of subunit, and that the library sourcing tests have all passed - if they 
+# have not, this test script may well fail strangely.
+
+# import the library.
+. ${SHELL_SHARE}subunit.sh
+
+echo 'test: subunit_start_test output'
+func_output=$(subunit_start_test "foo bar")
+func_status=$?
+if [ $func_status == 0 -a "x$func_output" = "xtest: foo bar" ]; then
+  echo 'success: subunit_start_test output'
+else
+  echo 'failure: subunit_start_test output ['
+  echo 'got an error code or incorrect output:'
+  echo "exit: $func_status"
+  echo "output: '$func_output'"
+  echo ']' ;
+fi
+
+subunit_start_test "subunit_pass_test output"
+func_output=$(subunit_pass_test "foo bar")
+func_status=$?
+if [ $func_status == 0 -a "x$func_output" = "xsuccess: foo bar" ]; then
+  subunit_pass_test "subunit_pass_test output"
+else
+  echo 'failure: subunit_pass_test output ['
+  echo 'got an error code or incorrect output:'
+  echo "exit: $func_status"
+  echo "output: '$func_output'"
+  echo ']' ;
+fi
+
+subunit_start_test "subunit_fail_test output"
+func_output=$(subunit_fail_test "foo bar" <<END
+something
+  wrong
+here
+END
+)
+func_status=$?
+if [ $func_status == 0 -a "x$func_output" = "xfailure: foo bar [
+something
+  wrong
+here
+]" ]; then
+  subunit_pass_test "subunit_fail_test output"
+else
+  echo 'failure: subunit_fail_test output ['
+  echo 'got an error code or incorrect output:'
+  echo "exit: $func_status"
+  echo "output: '$func_output'"
+  echo ']' ;
+fi
+
+subunit_start_test "subunit_error_test output"
+func_output=$(subunit_error_test "foo bar" <<END
+something
+  died
+here
+END
+)
+func_status=$?
+if [ $func_status == 0 -a "x$func_output" = "xerror: foo bar [
+something
+  died
+here
+]" ]; then
+  subunit_pass_test "subunit_error_test output"
+else
+  subunit_fail_test "subunit_error_test output" <<END
+got an error code or incorrect output:
+exit: $func_status
+output: '$func_output'
+END
+fi
diff --git a/lib/subunit/shell/tests/test_source_library.sh b/lib/subunit/shell/tests/test_source_library.sh
new file mode 100755 (executable)
index 0000000..699f128
--- /dev/null
@@ -0,0 +1,108 @@
+#!/bin/bash
+#  subunit shell bindings.
+#  Copyright (C) 2006  Robert Collins <robertc@robertcollins.net>
+#
+#  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
+#  license at the users choice. A copy of both licenses are available in the
+#  project source as Apache-2.0 and BSD. You may not use this file except in
+#  compliance with one of these two licences.
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+#  license you chose for the specific language governing permissions and
+#  limitations under that license.
+#
+
+
+# this script tests that we can source the subunit shell bindings successfully.
+# It manually implements the control protocol so that it des not depend on the
+# bindings being complete yet.
+
+# we expect to be run from the tree root.
+
+echo 'test: shell bindings can be sourced'
+# if any output occurs, this has failed to source cleanly
+source_output=$(. ${SHELL_SHARE}subunit.sh 2>&1)
+if [ $? == 0 -a "x$source_output" = "x" ]; then
+  echo 'success: shell bindings can be sourced'
+else
+  echo 'failure: shell bindings can be sourced ['
+  echo 'got an error code or output during sourcing.:'
+  echo $source_output
+  echo ']' ;
+fi
+
+# now source it for real
+. ${SHELL_SHARE}subunit.sh
+
+# we should have a start_test function
+echo 'test: subunit_start_test exists'
+found_type=$(type -t subunit_start_test)
+status=$?
+if [ $status == 0 -a "x$found_type" = "xfunction" ]; then
+  echo 'success: subunit_start_test exists'
+else
+  echo 'failure: subunit_start_test exists ['
+  echo 'subunit_start_test is not a function:'
+  echo "type -t status: $status"
+  echo "output: $found_type"
+  echo ']' ;
+fi
+
+# we should have a pass_test function
+echo 'test: subunit_pass_test exists'
+found_type=$(type -t subunit_pass_test)
+status=$?
+if [ $status == 0 -a "x$found_type" = "xfunction" ]; then
+  echo 'success: subunit_pass_test exists'
+else
+  echo 'failure: subunit_pass_test exists ['
+  echo 'subunit_pass_test is not a function:'
+  echo "type -t status: $status"
+  echo "output: $found_type"
+  echo ']' ;
+fi
+
+# we should have a fail_test function
+echo 'test: subunit_fail_test exists'
+found_type=$(type -t subunit_fail_test)
+status=$?
+if [ $status == 0 -a "x$found_type" = "xfunction" ]; then
+  echo 'success: subunit_fail_test exists'
+else
+  echo 'failure: subunit_fail_test exists ['
+  echo 'subunit_fail_test is not a function:'
+  echo "type -t status: $status"
+  echo "output: $found_type"
+  echo ']' ;
+fi
+
+# we should have a error_test function
+echo 'test: subunit_error_test exists'
+found_type=$(type -t subunit_error_test)
+status=$?
+if [ $status == 0 -a "x$found_type" = "xfunction" ]; then
+  echo 'success: subunit_error_test exists'
+else
+  echo 'failure: subunit_error_test exists ['
+  echo 'subunit_error_test is not a function:'
+  echo "type -t status: $status"
+  echo "output: $found_type"
+  echo ']' ;
+fi
+
+# we should have a skip_test function
+echo 'test: subunit_skip_test exists'
+found_type=$(type -t subunit_skip_test)
+status=$?
+if [ $status == 0 -a "x$found_type" = "xfunction" ]; then
+  echo 'success: subunit_skip_test exists'
+else
+  echo 'failure: subunit_skip_test exists ['
+  echo 'subunit_skip_test is not a function:'
+  echo "type -t status: $status"
+  echo "output: $found_type"
+  echo ']' ;
+fi
+
diff --git a/lib/subunit/update.sh b/lib/subunit/update.sh
deleted file mode 100755 (executable)
index f8265b1..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/sh
-# Pull in a new snapshot of Subunit from the upstream bzr branch
-
-TARGETDIR="`dirname $0`"
-WORKDIR="`mktemp -d`"
-bzr export "$WORKDIR/subunit" lp:subunit 
-bzr export "$WORKDIR/testtools" lp:testtools 
-
-for p in python/ filters/tap2subunit;
-do
-       rsync -avz --delete "$WORKDIR/subunit/$p" "$TARGETDIR/$p"
-done
-
-rsync -avz --delete "$WORKDIR/testtools/testtools/" "$TARGETDIR/python/testtools/"
-
-rm -rf "$WORKDIR"
diff --git a/lib/testtools/HACKING b/lib/testtools/HACKING
new file mode 100644 (file)
index 0000000..8fe323c
--- /dev/null
@@ -0,0 +1,139 @@
+===================================
+Notes for contributing to testtools
+===================================
+
+Coding style
+------------
+
+In general, follow PEP 8 <http://www.python.org/dev/peps/pep-0008/>.
+
+For consistency with the standard library's ``unittest`` module, method names
+are generally ``camelCase``.
+
+testtools supports Python 2.4 and later, so avoid any 2.5-only features like
+the ``with`` statement.
+
+
+Copyright assignment
+--------------------
+
+Part of testtools raison d'etre is to provide Python with improvements to the
+testing code it ships. For that reason we require all contributions (that are
+non-trivial) to meet one of the following rules:
+
+ - be inapplicable for inclusion in Python.
+ - be able to be included in Python without further contact with the
+   contributor.
+ - be copyright assigned to Jonathan M. Lange.
+
+Please pick one of these and specify it when contributing code to testtools.
+
+
+Licensing
+---------
+
+All code that is not copyright assigned to Jonathan M. Lange (see Copyright
+Assignment above) needs to be licensed under the MIT license that testtools
+uses, so that testtools can ship it.
+
+
+Testing
+-------
+
+Please write tests for every feature.  This project ought to be a model
+example of well-tested Python code!
+
+Take particular care to make sure the *intent* of each test is clear.
+
+You can run tests with ``make check``, or by running ``./run-tests`` directly.
+
+
+Source layout
+-------------
+
+The top-level directory contains the ``testtools/`` package directory, and
+miscellaneous files like README and setup.py.
+
+The ``testtools/`` directory is the Python package itself.  It is separated
+into submodules for internal clarity, but all public APIs should be “promoted”
+into the top-level package by importing them in ``testtools/__init__.py``.
+Users of testtools should never import a submodule, they are just
+implementation details.
+
+Tests belong in ``testtools/tests/``.
+
+
+Commiting to trunk
+------------------
+
+Testtools is maintained using bzr, with its trunk at lp:testtools. This gives
+every contributor the ability to commit their work to their own branches.
+However permission must be granted to allow contributors to commit to the trunk
+branch.
+
+Commit access to trunk is obtained by joining the testtools-devs Launchpad
+team. Membership in this team is contingent on obeying the testtools
+contribution policy, including assigning copyright of all the work one creates
+and places in trunk to Jonathan Lange.
+
+
+Code Review
+-----------
+
+All code must be reviewed before landing on trunk. The process is to create a
+branch in launchpad, and submit it for merging to lp:testtools. It will then
+be reviewed before it can be merged to trunk. It will be reviewed by someone:
+
+ * not the author
+ * a committer (member of the testtools-devs team)
+
+As a special exception, while the testtools committers team is small and prone
+to blocking, a merge request from a committer that has not been reviewed after
+24 hours may be merged by that committer. When the team is larger this policy
+will be revisited.
+
+Code reviewers should look for the quality of what is being submitted,
+including conformance with this HACKING file.
+
+Changes which all users should be made aware of should be documented in NEWS.
+
+
+NEWS management
+---------------
+
+The file NEWS is structured as a sorted list of releases. Each release can have
+a free form description and more or more sections with bullet point items.
+Sections in use today are 'Improvements' and 'Changes'. To ease merging between
+branches, the bullet points are kept alphabetically sorted. The release NEXT is
+permanently present at the top of the list.
+
+
+Release tasks
+-------------
+
+In no particular order:
+
+* Choose a version number.
+
+* Ensure __init__ has that version.
+
+* Add a version number to NEWS immediately below NEXT.
+
+* Possibly write a blurb into NEWS.
+
+* Replace any additional references to NEXT with the version being released.
+
+* Create a source distribution and upload to pypi ('make release').
+
+* Upload to Launchpad as well.
+
+* If a new series has been created (e.g. 0.10.0), make the series on Launchpad.
+
+* Merge or push the release branch to trunk.
+
+* Make a new milestone for the *next release*. We don't really know how we want
+  to handle these yet, so this is a suggestion not actual practice:
+
+  * during release we rename NEXT to $version.
+
+  * we call new milestones NEXT.
diff --git a/lib/testtools/LICENSE b/lib/testtools/LICENSE
new file mode 100644 (file)
index 0000000..bdc733f
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) 2008 Jonathan M. Lange <jml@mumak.net> and the testtools authors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/lib/testtools/MANIFEST.in b/lib/testtools/MANIFEST.in
new file mode 100644 (file)
index 0000000..3296ee4
--- /dev/null
@@ -0,0 +1,9 @@
+include LICENSE
+include HACKING
+include Makefile
+include MANIFEST.in
+include MANUAL
+include NEWS
+include README
+include run-tests
+include .bzrignore
diff --git a/lib/testtools/MANUAL b/lib/testtools/MANUAL
new file mode 100644 (file)
index 0000000..a040c28
--- /dev/null
@@ -0,0 +1,213 @@
+======
+Manual
+======
+
+Introduction
+------------
+
+This document provides overview of the features provided by testtools.  Refer
+to the API docs (i.e. docstrings) for full details on a particular feature.
+
+Extensions to TestCase
+----------------------
+
+Controlling test execution
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Testtools supports two ways to control how tests are executed. The simplest
+is to add a new exception to self.exception_handlers::
+
+    >>> self.exception_handlers.insert(-1, (ExceptionClass, handler)).
+
+Having done this, if any of setUp, tearDown, or the test method raise
+ExceptionClass, handler will be called with the test case, test result and the
+raised exception.
+
+Secondly, by overriding __init__ to pass in runTest=RunTestFactory the whole
+execution of the test can be altered. The default is testtools.runtest.RunTest
+and calls  case._run_setup, case._run_test_method and finally
+case._run_teardown. Other methods to control what RunTest is used may be
+added in future.
+
+
+TestCase.addCleanup
+~~~~~~~~~~~~~~~~~~~
+
+addCleanup is a robust way to arrange for a cleanup function to be called
+before tearDown.  This is a powerful and simple alternative to putting cleanup
+logic in a try/finally block or tearDown method.  e.g.::
+
+    def test_foo(self):
+        foo.lock()
+        self.addCleanup(foo.unlock)
+        ...
+
+
+TestCase.addOnException
+~~~~~~~~~~~~~~~~~~~~~~~
+
+addOnException adds an exception handler that will be called from the test
+framework when it detects an exception from your test code. The handler is
+given the exc_info for the exception, and can use this opportunity to attach
+more data (via the addDetails API) and potentially other uses.
+
+
+TestCase.skip
+~~~~~~~~~~~~~
+
+``skip`` is a simple way to have a test stop running and be reported as a
+skipped test, rather than a success/error/failure. This is an alternative to
+convoluted logic during test loading, permitting later and more localized
+decisions about the appropriateness of running a test. Many reasons exist to
+skip a test - for instance when a dependency is missing, or if the test is
+expensive and should not be run while on laptop battery power, or if the test
+is testing an incomplete feature (this is sometimes called a TODO). Using this
+feature when running your test suite with a TestResult object that is missing
+the ``addSkip`` method will result in the ``addError`` method being invoked
+instead.
+
+
+New assertion methods
+~~~~~~~~~~~~~~~~~~~~~
+
+testtools adds several assertion methods:
+
+ * assertIn
+ * assertNotIn
+ * assertIs
+ * assertIsNot
+ * assertIsInstance
+ * assertThat
+
+
+Improved assertRaises
+~~~~~~~~~~~~~~~~~~~~~
+
+TestCase.assertRaises returns the caught exception.  This is useful for
+asserting more things about the exception than just the type::
+
+        error = self.assertRaises(UnauthorisedError, thing.frobnicate)
+        self.assertEqual('bob', error.username)
+        self.assertEqual('User bob cannot frobnicate', str(error))
+
+
+TestCase.assertThat
+~~~~~~~~~~~~~~~~~~~
+
+assertThat is a clean way to write complex assertions without tying them to
+the TestCase inheritance hierarchy (and thus making them easier to reuse).
+
+assertThat takes an object to be matched, and a matcher, and fails if the
+matcher does not match the matchee.
+
+See pydoc testtools.Matcher for the protocol that matchers need to implement.
+
+testtools includes some matchers in testtools.matchers.
+python -c 'import testtools.matchers; print testtools.matchers.__all__' will
+list those matchers.
+
+An example using the DocTestMatches matcher which uses doctests example
+matching logic::
+
+    def test_foo(self):
+        self.assertThat([1,2,3,4], DocTestMatches('[1, 2, 3, 4]'))
+
+
+Creation methods
+~~~~~~~~~~~~~~~~
+
+testtools.TestCase implements creation methods called ``getUniqueString`` and
+``getUniqueInteger``.  See pages 419-423 of *xUnit Test Patterns* by Meszaros
+for a detailed discussion of creation methods.
+
+
+Test renaming
+~~~~~~~~~~~~~
+
+``testtools.clone_test_with_new_id`` is a function to copy a test case
+instance to one with a new name.  This is helpful for implementing test
+parameterization.
+
+
+Extensions to TestResult
+------------------------
+
+TestResult.addSkip
+~~~~~~~~~~~~~~~~~~
+
+This method is called on result objects when a test skips. The
+``testtools.TestResult`` class records skips in its ``skip_reasons`` instance
+dict. The can be reported on in much the same way as succesful tests.
+
+
+TestResult.time
+~~~~~~~~~~~~~~~
+
+This method controls the time used by a TestResult, permitting accurate
+timing of test results gathered on different machines or in different threads.
+See pydoc testtools.TestResult.time for more details.
+
+
+ThreadsafeForwardingResult
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A TestResult which forwards activity to another test result, but synchronises
+on a semaphore to ensure that all the activity for a single test arrives in a
+batch. This allows simple TestResults which do not expect concurrent test
+reporting to be fed the activity from multiple test threads, or processes.
+
+Note that when you provide multiple errors for a single test, the target sees
+each error as a distinct complete test.
+
+
+TextTestResult
+~~~~~~~~~~~~~~
+
+A TestResult that provides a text UI very similar to the Python standard
+library UI. Key differences are that its supports the extended outcomes and
+details API, and is completely encapsulated into the result object, permitting
+it to be used without a 'TestRunner' object. Not all the Python 2.7 outcomes
+are displayed (yet). It is also a 'quiet' result with no dots or verbose mode.
+These limitations will be corrected soon.
+
+
+Test Doubles
+~~~~~~~~~~~~
+
+In testtools.testresult.doubles there are three test doubles that testtools
+uses for its own testing: Python26TestResult, Python27TestResult,
+ExtendedTestResult. These TestResult objects implement a single variation of
+the TestResult API each, and log activity to a list self._events. These are
+made available for the convenience of people writing their own extensions.
+
+
+startTestRun and stopTestRun
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Python 2.7 added hooks 'startTestRun' and 'stopTestRun' which are called
+before and after the entire test run. 'stopTestRun' is particularly useful for
+test results that wish to produce summary output.
+
+testtools.TestResult provides empty startTestRun and stopTestRun methods, and
+the default testtools runner will call these methods appropriately.
+
+
+Extensions to TestSuite
+-----------------------
+
+ConcurrentTestSuite
+~~~~~~~~~~~~~~~~~~~
+
+A TestSuite for parallel testing. This is used in conjuction with a helper that
+runs a single suite in some parallel fashion (for instance, forking, handing
+off to a subprocess, to a compute cloud, or simple threads).
+ConcurrentTestSuite uses the helper to get a number of separate runnable
+objects with a run(result), runs them all in threads using the
+ThreadsafeForwardingResult to coalesce their activity.
+
+
+Running tests
+-------------
+
+Testtools provides a convenient way to run a test suite using the testtools
+result object: python -m testtools.run testspec [testspec...].
diff --git a/lib/testtools/Makefile b/lib/testtools/Makefile
new file mode 100644 (file)
index 0000000..5e232e3
--- /dev/null
@@ -0,0 +1,28 @@
+# See README for copyright and licensing details.
+
+PYTHON=python
+SOURCES=$(shell find testtools -name "*.py")
+
+check:
+       PYTHONPATH=$(PWD) $(PYTHON) -m testtools.run testtools.tests.test_suite
+
+TAGS: ${SOURCES}
+       ctags -e -R testtools/
+
+tags: ${SOURCES}
+       ctags -R testtools/
+
+clean:
+       rm -f TAGS tags
+       find testtools -name "*.pyc" -exec rm '{}' \;
+
+release:
+       ./setup.py sdist upload --sign
+
+apidocs:
+       pydoctor --make-html --add-package testtools \
+               --docformat=restructuredtext --project-name=testtools \
+               --project-url=https://launchpad.net/testtools
+
+
+.PHONY: check clean release apidocs
diff --git a/lib/testtools/NEWS b/lib/testtools/NEWS
new file mode 100644 (file)
index 0000000..90d7fc4
--- /dev/null
@@ -0,0 +1,191 @@
+testtools NEWS
+++++++++++++++
+
+NEXT
+~~~~
+
+Improvements
+------------
+
+* New matcher "Annotate" that adds a simple string message to another matcher,
+  much like the option 'message' parameter to standard library assertFoo
+  methods.
+
+* New matchers "Not" and "MatchesAll". "Not" will invert another matcher, and
+  "MatchesAll" that needs a successful match for all of its arguments.
+
+* On Python 2.4, where types.FunctionType cannot be deepcopied, testtools will
+  now monkeypatch copy._deepcopy_dispatch using the same trivial patch that
+  added such support to Python 2.5. The monkey patch is triggered by the
+  absence of FunctionType from the dispatch dict rather than a version check.
+  Bug #498030.
+
+* On windows the test 'test_now_datetime_now' should now work reliably.
+
+* TestCase.getUniqueInteger and TestCase.getUniqueString now have docstrings.
+
+* TestCase.getUniqueString now takes an optional prefix parameter, so you can
+  now use it in circumstances that forbid strings with '.'s, and such like.
+
+* testtools.testcase.clone_test_with_new_id now uses copy.copy, rather than
+  copy.deepcopy. Tests that need a deeper copy should use the copy protocol to
+  control how they are copied. Bug #498869.
+
+* The backtrace test result output tests should now pass on windows and other
+  systems where os.sep is not '/'.
+
+
+0.9.2
+~~~~~
+
+Python 3 support, more matchers and better consistency with Python 2.7 --
+you'd think that would be enough for a point release. Well, we here on the
+testtools project think that you deserve more.
+
+We've added a hook so that user code can be called just-in-time whenever there
+is an exception, and we've also factored out the "run" logic of test cases so
+that new outcomes can be added without fiddling with the actual flow of logic.
+
+It might sound like small potatoes, but it's changes like these that will
+bring about the end of test frameworks.
+
+
+Improvements
+------------
+
+* A failure in setUp and tearDown now report as failures not as errors.
+
+* Cleanups now run after tearDown to be consistent with Python 2.7's cleanup
+  feature.
+
+* ExtendedToOriginalDecorator now passes unrecognised attributes through
+  to the decorated result object, permitting other extensions to the
+  TestCase -> TestResult protocol to work.
+
+* It is now possible to trigger code just-in-time after an exception causes
+  a test outcome such as failure or skip. See the testtools MANUAL or
+  ``pydoc testtools.TestCase.addOnException``. (bug #469092)
+
+* New matcher Equals which performs a simple equality test.
+
+* New matcher MatchesAny which looks for a match of any of its arguments.
+
+* TestCase no longer breaks if a TestSkipped exception is raised with no
+  parameters.
+
+* TestCase.run now clones test cases before they are run and runs the clone.
+  This reduces memory footprint in large test runs - state accumulated on
+  test objects during their setup and execution gets freed when test case
+  has finished running unless the TestResult object keeps a reference.
+  NOTE: As test cloning uses deepcopy, this can potentially interfere if
+  a test suite has shared state (such as the testscenarios or testresources
+  projects use).  Use the __deepcopy__ hook to control the copying of such
+  objects so that the shared references stay shared.
+
+* Testtools now accepts contributions without copyright assignment under some
+  circumstances. See HACKING for details.
+
+* Testtools now provides a convenient way to run a test suite using the
+  testtools result object: python -m testtools.run testspec [testspec...].
+
+* Testtools now works on Python 3, thanks to Benjamin Peterson.
+
+* Test execution now uses a separate class, testtools.RunTest to run single
+  tests. This can be customised and extended in a more consistent fashion than
+  the previous run method idiom. See pydoc for more information.
+
+* The test doubles that testtools itself uses are now available as part of
+  the testtools API in testtols.testresult.doubles.
+
+* TracebackContent now sets utf8 as the charset encoding, rather than not
+  setting one and encoding with the default encoder.
+
+* With python2.7 testtools.TestSkipped will be the unittest.case.SkipTest
+  exception class making skips compatible with code that manually raises the
+  standard library exception. (bug #490109)
+
+Changes
+-------
+
+* TestCase.getUniqueInteger is now implemented using itertools.count. Thanks
+  to Benjamin Peterson for the patch. (bug #490111)
+
+
+0.9.1
+~~~~~
+
+The new matcher API introduced in 0.9.0 had a small flaw where the matchee
+would be evaluated twice to get a description of the mismatch. This could lead
+to bugs if the act of matching caused side effects to occur in the matchee.
+Since having such side effects isn't desirable, we have changed the API now
+before it has become widespread.
+
+Changes
+-------
+
+* Matcher API changed to avoid evaluating matchee twice. Please consult
+  the API documentation.
+
+* TestCase.getUniqueString now uses the test id, not the test method name,
+  which works nicer with parameterised tests.
+
+Improvements
+------------
+
+* Python2.4 is now supported again.
+
+
+0.9.0
+~~~~~
+
+This release of testtools is perhaps the most interesting and exciting one
+it's ever had. We've continued in bringing together the best practices of unit
+testing from across a raft of different Python projects, but we've also
+extended our mission to incorporating unit testing concepts from other
+languages and from our own research, led by Robert Collins.
+
+We now support skipping and expected failures. We'll make sure that you
+up-call setUp and tearDown, avoiding unexpected testing weirdnesses. We're
+now compatible with Python 2.5, 2.6 and 2.7 unittest library.
+
+All in all, if you are serious about unit testing and want to get the best
+thinking from the whole Python community, you should get this release.
+
+Improvements
+------------
+
+* A new TestResult API has been added for attaching details to test outcomes.
+  This API is currently experimental, but is being prepared with the intent
+  of becoming an upstream Python API. For more details see pydoc
+  testtools.TestResult and the TestCase addDetail / getDetails methods.
+
+* assertThat has been added to TestCase. This new assertion supports
+  a hamcrest-inspired matching protocol. See pydoc testtools.Matcher for
+  details about writing matchers, and testtools.matchers for the included
+  matchers. See http://code.google.com/p/hamcrest/.
+
+* Compatible with Python 2.6 and Python 2.7
+
+* Failing to upcall in setUp or tearDown will now cause a test failure.
+  While the base methods do nothing, failing to upcall is usually a problem
+  in deeper hierarchies, and checking that the root method is called is a
+  simple way to catch this common bug.
+
+* New TestResult decorator ExtendedToOriginalDecorator which handles
+  downgrading extended API calls like addSkip to older result objects that
+  do not support them. This is used internally to make testtools simpler but
+  can also be used to simplify other code built on or for use with testtools.
+
+* New TextTestResult supporting the extended APIs that testtools provides.
+
+* Nose will no longer find 'runTest' tests in classes derived from
+   testtools.testcase.TestCase (bug #312257).
+
+* Supports the Python 2.7/3.1 addUnexpectedSuccess and addExpectedFailure
+  TestResult methods, with a support function 'knownFailure' to let tests
+  trigger these outcomes.
+
+* When using the skip feature with TestResult objects that do not support it
+  a test success will now be reported. Previously an error was reported but
+  production experience has shown that this is too disruptive for projects that
+  are using skips: they cannot get a clean run on down-level result objects.
diff --git a/lib/testtools/README b/lib/testtools/README
new file mode 100644 (file)
index 0000000..5e3dd07
--- /dev/null
@@ -0,0 +1,54 @@
+=========
+testtools
+=========
+
+testtools is a set of extensions to the Python standard library's unit testing
+framework.
+
+These extensions have been derived from years of experience with unit testing
+in Python and come from many different sources.
+
+Licensing
+---------
+
+This project is distributed under the MIT license and copyright is owned by
+Jonathan M. Lange. See LICENSE for details.
+
+
+Dependencies
+------------
+
+ * Python 2.4+ or 3.0+
+
+
+Bug reports and patches
+-----------------------
+
+Please report bugs using Launchpad at <https://bugs.launchpad.net/testtools>.
+Patches can also be submitted via Launchpad, or mailed to the author.  You can
+mail the author directly at jml@mumak.net.
+
+There's no mailing list for this project yet, however the testing-in-python
+mailing list may be a useful resource:
+
+ * Address: testing-in-python@lists.idyll.org
+ * Subscription link: http://lists.idyll.org/listinfo/testing-in-python
+
+
+History
+-------
+
+testtools used to be called 'pyunit3k'.  The name was changed to avoid
+conflating the library with the Python 3.0 release (commonly referred to as
+'py3k').
+
+
+Thanks
+------
+
+ * Canonical Ltd
+ * Bazaar
+ * Twisted Matrix Labs
+ * Robert Collins
+ * Andrew Bennetts
+ * Benjamin Peterson
diff --git a/lib/testtools/setup.py b/lib/testtools/setup.py
new file mode 100755 (executable)
index 0000000..d7ed46f
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+"""Distutils installer for testtools."""
+
+from distutils.core import setup
+import testtools
+version = '.'.join(str(component) for component in testtools.__version__[0:3])
+phase = testtools.__version__[3]
+if phase != 'final':
+    import bzrlib.workingtree
+    t = bzrlib.workingtree.WorkingTree.open_containing(__file__)[0]
+    if phase == 'alpha':
+        # No idea what the next version will be
+        version = 'next-%s' % t.branch.revno()
+    else:
+        # Preserve the version number but give it a revno prefix
+        version = version + '~%s' % t.branch.revno()
+
+setup(name='testtools',
+      author='Jonathan M. Lange',
+      author_email='jml+testtools@mumak.net',
+      url='https://launchpad.net/testtools',
+      description=('Extensions to the Python standard library unit testing '
+                   'framework'),
+      version=version,
+      packages=['testtools', 'testtools.testresult', 'testtools.tests'])
index 832f11fafc3dc6674dcb7d9ae300c16c3e7782e6..86b3dd0e60c460741d094996f223d073912a8da8 100644 (file)
@@ -81,10 +81,32 @@ static void subunit_warning(struct torture_context *test,
        fprintf(stderr, "WARNING!: %s\n", comment);
 }
 
+static void subunit_progress(struct torture_context *tctx, int offset, enum torture_progress_whence whence)
+{
+       switch (whence) {
+       case TORTURE_PROGRESS_SET:
+               printf("progress: %d\n", offset);
+               break;
+       case TORTURE_PROGRESS_CUR:
+               printf("progress: %+-d\n", offset);
+               break;
+       case TORTURE_PROGRESS_POP:
+               printf("progress: pop\n");
+               break;
+       case TORTURE_PROGRESS_PUSH:
+               printf("progress: push\n");
+               break;
+       default:
+               fprintf(stderr, "Invalid call to progress()\n");
+               break;
+       }
+}
+
 const struct torture_ui_ops torture_subunit_ui_ops = {
        .comment = subunit_comment,
        .warning = subunit_warning,
        .test_start = subunit_test_start,
        .test_result = subunit_test_result,
-       .suite_start = subunit_suite_start
+       .suite_start = subunit_suite_start,
+       .progress = subunit_progress,
 };
index 672726ebf2fce08b655aa25899d17d685d39c6cd..9adf6816b838628b9bce06c62128cb2be17ab200 100644 (file)
@@ -245,6 +245,23 @@ struct torture_tcase *torture_suite_add_tcase(struct torture_suite *suite,
        return tcase;
 }
 
+int torture_suite_children_count(const struct torture_suite *suite)
+{
+       int ret = 0;
+       struct torture_tcase *tcase;
+       struct torture_test *test;
+       struct torture_suite *tsuite;
+       for (tcase = suite->testcases; tcase; tcase = tcase->next) {
+               for (test = tcase->tests; test; test = test->next) {
+                       ret++;
+               }
+       }
+       for (tsuite = suite->children; tsuite; tsuite = tsuite->next) {
+               ret ++;
+       }
+       return ret;
+}
+
 /**
  * Run a torture test suite.
  */
@@ -259,6 +276,8 @@ bool torture_run_suite(struct torture_context *context,
        if (context->results->ui_ops->suite_start)
                context->results->ui_ops->suite_start(context, suite);
 
+       context->results->ui_ops->progress(context, 
+               torture_suite_children_count(suite), TORTURE_PROGRESS_SET); 
        old_testname = context->active_testname;
        if (old_testname != NULL)
                context->active_testname = talloc_asprintf(context, "%s-%s", 
@@ -271,7 +290,9 @@ bool torture_run_suite(struct torture_context *context,
        }
 
        for (tsuite = suite->children; tsuite; tsuite = tsuite->next) {
+               context->results->ui_ops->progress(context, 0, TORTURE_PROGRESS_PUSH);
                ret &= torture_run_suite(context, tsuite);
+               context->results->ui_ops->progress(context, 0, TORTURE_PROGRESS_POP);
        }
 
        talloc_free(context->active_testname);
index ad3668fbe87cebeff4a65090e473b6aae8c71a2a..cb2c02ba3e0e5de1ec86f8f4280be643e5243336 100644 (file)
@@ -34,6 +34,13 @@ enum torture_result {
        TORTURE_SKIP=3
 };
 
+enum torture_progress_whence {
+       TORTURE_PROGRESS_SET,
+       TORTURE_PROGRESS_CUR,
+       TORTURE_PROGRESS_POP,
+       TORTURE_PROGRESS_PUSH,
+};
+
 /* 
  * These callbacks should be implemented by any backend that wishes 
  * to listen to reports from the torture tests.
@@ -52,6 +59,7 @@ struct torture_ui_ops
                                                struct torture_test *);
        void (*test_result) (struct torture_context *, 
                                                 enum torture_result, const char *reason);
+       void (*progress) (struct torture_context *, int offset, enum torture_progress_whence whence);
 };
 
 void torture_ui_test_start(struct torture_context *context,
@@ -465,6 +473,7 @@ struct torture_test *torture_tcase_add_simple_test(struct torture_tcase *tcase,
 bool torture_suite_init_tcase(struct torture_suite *suite, 
                              struct torture_tcase *tcase, 
                              const char *name);
+int torture_suite_children_count(const struct torture_suite *suite);
 
 struct torture_context *torture_context_init(struct tevent_context *event_ctx, struct torture_results *results);
 
diff --git a/lib/update-external.sh b/lib/update-external.sh
new file mode 100755 (executable)
index 0000000..53748d8
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+# Pull in a new snapshot of external projects that are included in 
+# our source tree for users that don't have them installed on their system
+
+TARGETDIR="`dirname $0`"
+WORKDIR="`mktemp -d`"
+
+echo "Updating subunit..."
+bzr export "$WORKDIR/subunit" lp:subunit 
+rsync -avz --delete "$WORKDIR/subunit/" "$TARGETDIR/subunit/"
+
+echo "Updating testtools..."
+bzr export "$WORKDIR/testtools" lp:testtools 
+rsync -avz --delete "$WORKDIR/testtools/" "$TARGETDIR/testtools/"
+
+rm -rf "$WORKDIR"
index 2a9fc0e48b8de8a982fe46bdd8b8dd7d843a0637..42a9ad08aafaf0ba3e3283d70c7fdf67bcbaeab7 100644 (file)
@@ -95,8 +95,6 @@ sub parse_results($$$)
                        } 
                } elsif (/^testsuite: (.*)\n/) {
                        $msg_ops->start_testsuite($1);
-               } elsif (/^testsuite-count: (\d+)\n/) {
-                       $msg_ops->testsuite_count($1);
                } else {
                        $msg_ops->output_msg($_);
                }
@@ -176,6 +174,27 @@ sub report_time($)
        printf "time: %04d-%02d-%02d %02d:%02d:%02d\n", $year+1900, $mon+1, $mday, $hour, $min, $sec;
 }
 
+sub progress_pop()
+{
+       print "progress: pop\n";
+}
+
+sub progress_push()
+{
+       print "progress: push\n";
+}
+
+sub progress($;$)
+{
+       my ($count, $whence) = @_;
+
+       unless(defined($whence)) {
+               $whence = "";
+       }
+
+       print "progress: $whence$count\n";
+}
+
 # The following are Samba extensions:
 
 sub start_testsuite($)
@@ -208,10 +227,4 @@ sub end_testsuite($$;$)
        }
 }
 
-sub testsuite_count($)
-{
-       my ($count) = @_;
-       print "testsuite-count: $count\n";
-}
-
 1;
diff --git a/selftest/filter-subunit b/selftest/filter-subunit
new file mode 100755 (executable)
index 0000000..605a898
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+# Filter a subunit stream
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org>
+# Published under the GNU GPL, v3 or later
+
+import optparse
+import os
+import sys
+import signal
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../lib/subunit/python"))
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../lib/testtools"))
+
+import subunithelper
+
+parser = optparse.OptionParser("filter-subunit [options] < instream > outstream")
+parser.add_option("--expected-failures", type="string", 
+       help="File containing list of regexes matching tests to consider known "
+            "failures")
+parser.add_option("--strip-passed-output", action="store_true", 
+    help="Whether to strip output from tests that passed")
+
+parser.add_option("--prefix", type="string",
+       help="Add prefix to all test names")
+
+opts, args = parser.parse_args()
+
+if opts.expected_failures:
+       expected_failures = list(subunithelper.read_test_regexes(opts.expected_failures))
+else:
+       expected_failures = []
+
+statistics = {
+       'TESTS_UNEXPECTED_OK': 0,
+       'TESTS_EXPECTED_OK': 0,
+       'TESTS_UNEXPECTED_FAIL': 0,
+       'TESTS_EXPECTED_FAIL': 0,
+       'TESTS_ERROR': 0,
+       'TESTS_SKIP': 0,
+}
+
+def handle_sigint(sig, stack):
+       sys.exit(0)
+signal.signal(signal.SIGINT, handle_sigint)
+
+msg_ops = subunithelper.FilterOps(opts.prefix, expected_failures, 
+       opts.strip_passed_output)
+
+sys.exit(subunithelper.parse_results(msg_ops, statistics, sys.stdin))
diff --git a/selftest/filter-subunit.pl b/selftest/filter-subunit.pl
deleted file mode 100755 (executable)
index 5e87ef4..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/usr/bin/perl
-# Filter a subunit stream
-# Copyright (C) Jelmer Vernooij <jelmer@samba.org>
-# Published under the GNU GPL, v3 or later
-
-=pod
-
-=head1 NAME
-
-filter-subunit - Filter a subunit stream
-
-=head1 SYNOPSIS
-
-filter-subunit --help
-
-filter-subunit --prefix=PREFIX --known-failures=FILE < in-stream > out-stream
-
-=head1 DESCRIPTION
-
-Simple Subunit stream filter that will change failures to known failures 
-based on a list of regular expressions.
-
-=head1 OPTIONS
-
-=over 4
-
-=item I<--prefix>
-
-Add the specified prefix to all test names.
-
-=item I<--expected-failures>
-
-Specify a file containing a list of tests that are expected to fail. Failures 
-for these tests will be counted as successes, successes will be counted as 
-failures.
-
-The format for the file is, one entry per line:
-
-TESTSUITE-NAME.TEST-NAME
-
-The reason for a test can also be specified, by adding a hash sign (#) and the reason 
-after the test name.
-
-=head1 LICENSE
-
-selftest is licensed under the GNU General Public License L<http://www.gnu.org/licenses/gpl.html>.
-
-
-=head1 AUTHOR
-
-Jelmer Vernooij
-
-=cut
-
-use Getopt::Long;
-use strict;
-use FindBin qw($RealBin $Script);
-use lib "$RealBin";
-use Subunit qw(parse_results);
-use Subunit::Filter;
-
-my $opt_expected_failures = undef;
-my $opt_help = 0;
-my $opt_prefix = undef;
-my $opt_strip_ok_output = 0;
-my @expected_failures = ();
-
-my $result = GetOptions(
-               'expected-failures=s' => \$opt_expected_failures,
-               'strip-passed-output' => \$opt_strip_ok_output,
-               'prefix=s' => \$opt_prefix,
-               'help' => \$opt_help,
-       );
-exit(1) if (not $result);
-
-if ($opt_help) {
-       print "Usage: filter-subunit [--prefix=PREFIX] [--expected-failures=FILE]... < instream > outstream\n";
-       exit(0);
-}
-
-if (defined($opt_expected_failures)) {
-       @expected_failures = Subunit::Filter::read_test_regexes($opt_expected_failures);
-}
-
-# we want unbuffered output
-$| = 1;
-
-my $statistics = {
-       TESTS_UNEXPECTED_OK => 0,
-       TESTS_EXPECTED_OK => 0,
-       TESTS_UNEXPECTED_FAIL => 0,
-       TESTS_EXPECTED_FAIL => 0,
-       TESTS_ERROR => 0,
-       TESTS_SKIP => 0,
-};
-
-my $msg_ops = new Subunit::Filter($opt_prefix, \@expected_failures, 
-                                     $opt_strip_ok_output);
-
-exit(parse_results($msg_ops, $statistics, *STDIN));
index 37470828390e8579a1510d3ed4ff9e1a8abd0df8..54949df97a9d55f109f537799dd3fa33bacaa7e0 100755 (executable)
@@ -6,9 +6,14 @@
 
 import optparse
 import os
+import signal
 import sys
 
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../lib/subunit/python"))
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../lib/testtools"))
+
 import subunithelper
+import subunit
 
 class PlainFormatter(object):
 
@@ -25,10 +30,19 @@ class PlainFormatter(object):
         self.summaryfile = summaryfile
         self.index = 0
         self.name = None
+        self._progress_level = 0
         self.totalsuites = totaltests
 
-    def testsuite_count(self, count):
-        self.totalsuites = count
+    def progress(self, offset, whence):
+        if whence == subunit.PROGRESS_POP:
+            self._progress_level -= 1
+        elif whence == subunit.PROGRESS_PUSH:
+            self._progress_level += 1
+        elif whence == subunit.PROGRESS_SET:
+            if self._progress_level == 0:
+                self.totalsuites = offset
+        elif whence == subunit.PROGRESS_CUR:
+            raise NotImplementedError
 
     def report_time(self, time):
         if self.start_time is None:
@@ -71,7 +85,7 @@ class PlainFormatter(object):
 
     def end_testsuite(self, name, result, reason):
         out = ""
-        unexpected = 0
+        unexpected = False
 
         if not name in self.test_output:
             print "no output for name[%s]" % name
@@ -79,11 +93,13 @@ class PlainFormatter(object):
         if result in ("success", "xfail"):
             self.suites_ok+=1
         else:
-            self.output_msg("ERROR: Testsuite[%s]\nREASON: %s\n" % (name, reason or ''))
+            self.output_msg("ERROR: Testsuite[%s]\n" % name)
+            if reason is not None:
+                self.output_msg("REASON: %s\n" % (reason,))
             self.suitesfailed.append(name)
             if self.immediate and not self.verbose:
                 out += self.test_output[name]
-            unexpected = 1
+            unexpected = True
 
         if not self.immediate:
             if not unexpected:
@@ -96,7 +112,7 @@ class PlainFormatter(object):
     def start_test(self, testname):
         pass
 
-    def end_test(self, testname, result, unexpected, reason):
+    def end_test(self, testname, result, unexpected, reason=None):
         if not unexpected:
             self.test_output[self.name] = ""
             if not self.immediate:
@@ -107,11 +123,9 @@ class PlainFormatter(object):
                     'success': '.'}.get(result, "?(%s)" % result))
             return
 
-        if reason is None:
-            reason = ''
-        reason = reason.strip()
-
-        self.test_output[self.name] += "UNEXPECTED(%s): %s\nREASON: %s\n" % (result, testname, reason)
+        self.test_output[self.name] += "UNEXPECTED(%s): %s\n" % (result, testname)
+        if reason is not None:
+            self.test_output[self.name] += "REASON: %s\n" % (reason.strip(),)
 
         if self.immediate and not self.verbose:
             print self.test_output[self.name]
@@ -188,6 +202,10 @@ statistics = {
     'TESTS_SKIP': 0,
 }
 
+def handle_sigint(sig, stack):
+       sys.exit(0)
+signal.signal(signal.SIGINT, handle_sigint)
+
 msg_ops = PlainFormatter(os.path.join(opts.prefix, "summary"), opts.verbose,
     opts.immediate, statistics)
 
index 7bbad62bbf8f2cb520d5c6ff6af193855495be61..634db92c9261b8154968bdac67b80f7143ea9900 100755 (executable)
@@ -229,6 +229,7 @@ sub run_testsuite($$$$$)
        my $pcap_file = setup_pcap($name);
 
        Subunit::start_testsuite($name);
+       Subunit::progress_push();
        Subunit::report_time(time());
 
        open(RESULTS, "$cmd 2>&1|");
@@ -249,6 +250,7 @@ sub run_testsuite($$$$$)
 
        unless (close(RESULTS)) {
                if ($!) {
+                       Subunit::progress_pop();
                        Subunit::end_testsuite($name, "error", "Unable to run $cmd: $!");
                        return 0;
                } else {
@@ -257,6 +259,7 @@ sub run_testsuite($$$$$)
        } 
 
        if ($ret & 127) {
+               Subunit::progress_pop();
                Subunit::end_testsuite($name, "error", sprintf("Testsuite died with signal %d, %s coredump", ($ret & 127), ($ret & 128) ? "with": "without"));
                return 0;
        }
@@ -271,6 +274,7 @@ sub run_testsuite($$$$$)
        my $exitcode = $ret >> 8;
 
        Subunit::report_time(time());
+       Subunit::progress_pop();
        if ($exitcode == 0) {
                Subunit::end_testsuite($name, "success");
        } else {
@@ -684,7 +688,7 @@ foreach my $fn (@testlists) {
        }
 }
 
-Subunit::testsuite_count($#available+1);
+Subunit::progress($#available+1);
 Subunit::report_time(time());
 
 foreach (@available) {
index 517bbe2c903583705c66af630ddc72aa3539c5f1..8659f984d87052245f1e57e18d5434e99658071e 100644 (file)
@@ -18,6 +18,8 @@
 __all__ = ['parse_results']
 
 import re
+import sys
+import subunit
 import time
 
 VALID_RESULTS = ['success', 'successful', 'failure', 'fail', 'skip', 'knownfail', 'error', 'xfail', 'skip-testsuite', 'testsuite-failure', 'testsuite-xfail', 'testsuite-success', 'testsuite-error']
@@ -101,8 +103,16 @@ def parse_results(msg_ops, statistics, fh):
                 msg_ops.end_testsuite(testname, "error", reason)
         elif l.startswith("testsuite: "):
             msg_ops.start_testsuite(l.split(":", 1)[1].strip())
-        elif l.startswith("testsuite-count: "):
-            msg_ops.testsuite_count(int(l.split(":", 1)[1].strip()))
+        elif l.startswith("progress: "):
+            arg = l.split(":", 1)[1].strip()
+            if arg == "pop":
+                msg_ops.progress(None, subunit.PROGRESS_POP)
+            elif arg == "push":
+                msg_ops.progress(None, subunit.PROGRESS_PUSH)
+            elif arg[0] in '+-':
+                msg_ops.progress(int(arg), subunit.PROGRESS_CUR)
+            else:
+                msg_ops.progress(int(arg), subunit.PROGRESS_SET)
         else:
             msg_ops.output_msg(l)
 
@@ -144,8 +154,21 @@ class SubunitOps(object):
         self.end_test(name, "xfail", reason)
 
     def report_time(self, t):
-        (sec, min, hour, mday, mon, year, wday, yday, isdst) = time.localtimet(t)
-        print "time: %04d-%02d-%02d %02d:%02d:%02d" % (year+1900, mon+1, mday, hour, min, sec)
+        (year, mon, mday, hour, min, sec, wday, yday, isdst) = time.localtime(t)
+        print "time: %04d-%02d-%02d %02d:%02d:%02d" % (year, mon, mday, hour, min, sec)
+
+    def progress(self, offset, whence):
+        if whence == subunit.PROGRESS_CUR and offset > -1:
+            prefix = "+"
+        elif whence == subunit.PROGRESS_PUSH:
+            prefix = ""
+            offset = "push"
+        elif whence == subunit.PROGRESS_POP:
+            prefix = ""
+            offset = "pop"
+        else:
+            prefix = ""
+        print "progress: %s%s" % (prefix, offset)
 
     # The following are Samba extensions:
     def start_testsuite(self, name):
@@ -159,11 +182,136 @@ class SubunitOps(object):
 
     def end_testsuite(self, name, result, reason=None):
         if reason:
-            print "testsuite-$result: %s [" % name
+            print "testsuite-%s: %s [" % (result, name)
             print "%s" % reason
             print "]"
         else:
-            print "testsuite-$result: %s" % name
+            print "testsuite-%s: %s" % (result, name)
+
+
+def read_test_regexes(name):
+    f = open(name, 'r')
+    try:
+        for l in f:
+            l = l.strip()
+            if l == "" or l[0] == "#":
+                continue
+            if "#" in l:
+                (regex, reason) = l.split("#", 1)
+                yield (regex.strip(), reason.strip())
+            else:
+                yield l, None
+    finally:
+        f.close()
+
+
+def find_in_list(regexes, fullname):
+    for regex, reason in regexes:
+        if re.match(regex, fullname):
+            if reason is None:
+                return ""
+            return reason
+    return None
+
+
+class FilterOps(object):
+
+    def control_msg(self, msg):
+        pass # We regenerate control messages, so ignore this
+
+    def report_time(self, time):
+        self._ops.report_time(time)
+
+    def progress(self, delta, whence):
+        self._ops.progress(delta, whence)
+
+    def output_msg(self, msg):
+        if self.output is None:
+            sys.stdout.write(msg)
+        else:
+            self.output+=msg
+
+    def start_test(self, testname):
+        if self.prefix is not None:
+            testname = self.prefix + testname
+
+        if self.strip_ok_output:
+           self.output = ""
+
+        self._ops.start_test(testname)
+
+    def end_test(self, testname, result, unexpected, reason):
+        if self.prefix is not None:
+            testname = self.prefix + testname
+
+        if result in ("fail", "failure") and not unexpected:
+            result = "xfail"
+            self.xfail_added+=1
+            self.total_xfail+=1
+        xfail_reason = find_in_list(self.expected_failures, testname)
+        if xfail_reason is not None and result in ("fail", "failure"):
+            result = "xfail"
+            self.xfail_added+=1
+            self.total_xfail+=1
+            reason += xfail_reason
+
+        if result in ("fail", "failure"):
+            self.fail_added+=1
+            self.total_fail+=1
+
+        if result == "error":
+            self.error_added+=1
+            self.total_error+=1
+
+        if self.strip_ok_output:
+            if result not in ("success", "xfail", "skip"):
+                print self.output
+        self.output = None
+
+        self._ops.end_test(testname, result, reason)
+
+    def skip_testsuite(self, name, reason=None):
+        self._ops.skip_testsuite(name, reason)
+
+    def start_testsuite(self, name):
+        self._ops.start_testsuite(name)
+
+        self.error_added = 0
+        self.fail_added = 0
+        self.xfail_added = 0
+
+    def end_testsuite(self, name, result, reason=None):
+        xfail = False
+
+        if self.xfail_added > 0:
+            xfail = True
+        if self.fail_added > 0 or self.error_added > 0:
+            xfail = False
+
+        if xfail and result in ("fail", "failure"):
+            result = "xfail"
+
+        if self.fail_added > 0 and result != "failure":
+            result = "failure"
+            if reason is None:
+                reason = "Subunit/Filter Reason"
+            reason += "\n failures[%d]" % self.fail_added
+
+        if self.error_added > 0 and result != "error":
+            result = "error"
+            if reason is None:
+                reason = "Subunit/Filter Reason"
+            reason += "\n errors[%d]" % self.error_added
+
+        self._ops.end_testsuite(name, result, reason)
 
-    def testsuite_count(self, count):
-        print "testsuite-count: %d" % count
+    def __init__(self, prefix, expected_failures, strip_ok_output):
+        self._ops = SubunitOps()
+        self.output = None
+        self.prefix = prefix
+        self.expected_failures = expected_failures
+        self.strip_ok_output = strip_ok_output
+        self.xfail_added = 0
+        self.total_xfail = 0
+        self.total_error = 0
+        self.total_fail = 0
index 97b3275e46fba5127ef137dd4d080505d7d09183..8cc6c66b239e5ee0ab1669509b00276c908834fe 100644 (file)
@@ -3251,7 +3251,7 @@ selftest:: all torture timelimit
                --testlist="$(srcdir)/selftest/tests.sh|" \
                --exclude=$(srcdir)/selftest/skip \
                --socket-wrapper $(TESTS) | \
-               $(PERL) $(selftestdir)/filter-subunit.pl \
+               $(PYTHON) $(selftestdir)/filter-subunit \
                --expected-failures=$(srcdir)/selftest/knownfail | \
                $(PYTHON) $(selftestdir)/format-subunit --immediate
 
index 6068816ad98ff19229cfd1a9b0743fed1e743fbe..dd120f9bd2785beb62556bdfe37c34d63ff6f45f 100644 (file)
@@ -2349,9 +2349,21 @@ static bool keepalive_fn(const struct timeval *now, void *private_data)
 static bool deadtime_fn(const struct timeval *now, void *private_data)
 {
        struct smbd_server_connection *sconn = smbd_server_conn;
+
+       if (sconn->allow_smb2) {
+               /* TODO: implement real idle check */
+               if (sconn->smb2.sessions.list) {
+                       return true;
+               }
+               DEBUG( 2, ( "Closing idle SMB2 connection\n" ) );
+               messaging_send(smbd_messaging_context(), procid_self(),
+                              MSG_SHUTDOWN, &data_blob_null);
+               return false;
+       }
+
        if ((conn_num_open(sconn) == 0)
            || (conn_idle_all(sconn, now->tv_sec))) {
-               DEBUG( 2, ( "Closing idle connection\n" ) );
+               DEBUG( 2, ( "Closing idle SMB1 connection\n" ) );
                messaging_send(smbd_messaging_context(), procid_self(),
                               MSG_SHUTDOWN, &data_blob_null);
                return False;
index 13bec7efae56569453e9bd3d925261b271855ab1..eff92c5f339e5c762881d512baa135e7fc2d2b37 100755 (executable)
@@ -8,6 +8,7 @@ import os
 
 sys.path.append("bin/python")
 sys.path.append("../lib/subunit/python")
+sys.path.append("../lib/testtools")
 
 import samba.getopt as options
 
index b8df072bf3f596263cb0c7e0793668dff4413460..28b3a5fa781a6c9ee69a146b527d2d98918f42b4 100755 (executable)
@@ -2,16 +2,13 @@
 # -*- coding: utf-8 -*-
 # This is a port of the original in testprogs/ejs/ldap.js
 
-import getopt
 import optparse
 import sys
-import time
-import random
-import base64
 import os
 
 sys.path.append("bin/python")
 sys.path.append("../lib/subunit/python")
+sys.path.append("../lib/testtools")
 
 import samba.getopt as options
 
@@ -26,9 +23,6 @@ from samba import glue
 from subunit.run import SubunitTestRunner
 import unittest
 
-from samba.ndr import ndr_pack, ndr_unpack
-from samba.dcerpc import security
-
 parser = optparse.OptionParser("urgent_replication [options] <host>")
 sambaopts = options.SambaOptions(parser)
 parser.add_option_group(sambaopts)
index eba5996e131044cd4352aa56a7a6dbcc32129135..6a53b988f5ce3593b941cb1c495bb15d3c41f49e 100755 (executable)
@@ -82,12 +82,13 @@ cp setup/provision.smb.conf.member $SETUPDIR || exit 1
 cp setup/provision.smb.conf.standalone $SETUPDIR || exit 1
 
 echo "Installing external python libraries"
-mkdir -p $PYTHONDIR/samba_external || exit 1
+mkdir -p $DESTDIR$PYTHONDIR || exit 1
 for p in $($PYTHON scripting/python/samba_external/missing.py);
 do
-  echo "Installing missing python library $p"
-  mkdir -p $PYTHONDIR/samba_external/$p
-  cp -r ../lib/$p/* $PYTHONDIR/samba_external/$p/ || exit 1
+  package=`basename $p`
+  echo "Installing missing python package $package"
+  mkdir -p $DESTDIR$PYTHONDIR/$package
+  cp -r ../lib/$p/* $DESTDIR$PYTHONDIR/$package/ || exit 1
 done
 
 echo "Installing stuff in $PRIVATEDIR"
index d36e136346045500d047d7928ed4be93640fd536..db35669e1ab22293ff68d05183497f1a1aa687d4 100644 (file)
@@ -23,9 +23,9 @@ python_glue_OBJ_FILES = $(pyscriptsrcdir)/pyglue.o
 
 $(python_glue_OBJ_FILES): CFLAGS+=-I$(ldbsrcdir)
 
-_PY_FILES = $(shell find $(pyscriptsrcdir)/samba ../lib/subunit/python -type f -name "*.py")
+_PY_FILES = $(shell find $(pyscriptsrcdir)/samba -type f -name "*.py")
 
-$(eval $(foreach pyfile, $(_PY_FILES),$(call python_py_module_template,$(patsubst $(pyscriptsrcdir)/%,%,$(subst ../lib/subunit/python,,$(pyfile))),$(pyfile))))
+$(eval $(foreach pyfile, $(_PY_FILES),$(call python_py_module_template,$(patsubst $(pyscriptsrcdir)/%,%,$(pyfile)),$(pyfile))))
 
 PYDOCTOR = pydoctor
 PYDOCTOR_OPTIONS = --project-name Samba --project-url http://www.samba.org/ \
index 50bda65a210296dc81811894332c51e0e980c637..d3dd2b9290f46744f7c7f4b0d3b327db4ac6ad6e 100755 (executable)
@@ -1,12 +1,17 @@
 #!/usr/bin/python
 
 # work out what python external libraries we need to install
+external_libs = {
+    "dns.resolver": "dnspython/dns", 
+    "subunit": "subunit/python/subunit",
+    "testtools": "testtools/testtools"}
 
 list = []
 
-try:
-    import dns.resolver
-except:
-    list.append("dnspython")
+for module, package in external_libs.iteritems():
+    try:
+        __import__(module)
+    except ImportError:
+        list.append(package)
 
 print ' '.join(list)
index 6057de68f9d77376c65ac234f6e0c1fb120487bd..c4c5e190a9443552c0c3cd8f0ab80644b82f894b 100644 (file)
@@ -13,7 +13,7 @@ ST_DONE_TEST = @test -f $(selftest_prefix)/st_done || { echo "SELFTEST FAILED";
 
 SELFTEST_NOSLOW_OPTS = --exclude=$(srcdir)/selftest/slow
 SELFTEST_QUICK_OPTS = $(SELFTEST_NOSLOW_OPTS) --quick --include=$(srcdir)/selftest/quick
-FILTER_XFAIL = $(PERL) $(selftestdir)/filter-subunit.pl --expected-failures=$(srcdir)/selftest/knownfail
+FILTER_XFAIL = $(PYTHON) $(selftestdir)/filter-subunit --expected-failures=$(srcdir)/selftest/knownfail
 SUBUNIT_FORMATTER ?= $(PYTHON) $(selftestdir)/format-subunit --prefix=${selftest_prefix} --immediate
 FORMAT_TEST_OUTPUT = $(FILTER_XFAIL) | $(SUBUNIT_FORMATTER)
 
index a6b7161019fd7611af363eddfdfb793b9ffc262a..60606ace565a9cbe1a223a217ac9f847462ade8a 100644 (file)
@@ -3,63 +3,63 @@
 #
 # "make test" will not report failures for tests listed here and will consider
 # a successful run for any of these tests an error.
-local.resolve.*.async
-local.iconv.*.next_codepoint()
-base.delete.*.deltest17
-base.delete.*.deltest20a
-base.delete.*.deltest20b
-raw.rename.*.osxrename
-raw.rename.*.directory rename
-rpc.winreg.*security
+samba4.local.resolve.*.async
+samba4.local.iconv.*.next_codepoint()
+samba4..*base.delete.*.deltest17
+samba4..*base.delete.*.deltest20a
+samba4..*base.delete.*.deltest20b
+samba4.raw.rename.*.osxrename
+samba4.raw.rename.*.directory rename
+samba4.rpc.winreg.*security
 samba4.local.registry.(dir|ldb).check hive security
 samba4.local.registry.local.security
-rpc.wkssvc
-rpc.handles.*.lsarpc-shared
-rpc.handles.*.mixed-shared
-rpc.epmapper.*.Insert
-rpc.epmapper.*.InqObject
-rpc.drsuapi.*
-rpc.lsalookup
-rpc.cracknames
-rpc.netlogon.*.LogonUasLogon
-rpc.netlogon.*.LogonUasLogoff
-rpc.netlogon.*.DatabaseSync
-rpc.netlogon.*.DatabaseSync2
-rpc.netlogon.*.LogonControl
-rpc.netlogon.*.LogonControl2
-rpc.netlogon.*.DsrEnumerateDomainTrusts
-rpc.netlogon.*.NetrEnumerateTrustedDomains
-rpc.netlogon.*.NetrEnumerateTrustedDomainsEx
-rpc.netlogon.*.DsrGetDcSiteCoverageW
-rpc.netlogon.*.DsRAddressToSitenamesW
-rpc.netlogon.*.DsRAddressToSitenamesExW
-rpc.netlogon.*.GetPassword
-rpc.netlogon.*.GetTrustPasswords
-rpc.netlogon.*.DatabaseRedo
-rpc.netlogon.*.ServerGetTrustInfo
-rpc.netlogon.*.GetDomainInfo # Also fails against W2K8 (but in a different way)
+samba4.rpc.wkssvc
+samba4.rpc.handles.*.lsarpc-shared
+samba4.rpc.handles.*.mixed-shared
+samba4.rpc.epmapper.*.Insert
+samba4.rpc.epmapper.*.InqObject
+samba4.rpc.drsuapi.*
+samba4.rpc.lsalookup
+samba4.rpc.cracknames
+samba4.rpc.netlogon.*.LogonUasLogon
+samba4.rpc.netlogon.*.LogonUasLogoff
+samba4.rpc.netlogon.*.DatabaseSync
+samba4.rpc.netlogon.*.DatabaseSync2
+samba4.rpc.netlogon.*.LogonControl
+samba4.rpc.netlogon.*.LogonControl2
+samba4.rpc.netlogon.*.DsrEnumerateDomainTrusts
+samba4.rpc.netlogon.*.NetrEnumerateTrustedDomains
+samba4.rpc.netlogon.*.NetrEnumerateTrustedDomainsEx
+samba4.rpc.netlogon.*.DsrGetDcSiteCoverageW
+samba4.rpc.netlogon.*.DsRAddressToSitenamesW
+samba4.rpc.netlogon.*.DsRAddressToSitenamesExW
+samba4.rpc.netlogon.*.GetPassword
+samba4.rpc.netlogon.*.GetTrustPasswords
+samba4.rpc.netlogon.*.DatabaseRedo
+samba4.rpc.netlogon.*.ServerGetTrustInfo
+samba4.rpc.netlogon.*.GetDomainInfo # Also fails against W2K8 (but in a different way)
 samba4.rpc.samr.passwords.pwdlastset # Not provided by Samba 4 yet
 samba4.rpc.samr.passwords.badpwdcount # Not provided by Samba 4 yet
 samba4.rpc.samr.passwords.lockout
 samba4.rpc.samr.users.privileges
 samba4.rpc.spoolss # Not provided by Samba 4 yet
-base.charset.*.Testing partial surrogate
+samba4.base.charset.*.Testing partial surrogate
 .*net.api.delshare.*                           # DelShare isn't implemented yet
 ^samba4.net.api.become.dc.*$                   # TODO: this should work!!!
-rap.*netservergetinfo
-smb2.persistent.handles1
+samba4.rap.*netservergetinfo
+samba4.smb2.persistent.handles1
 samba4.winbind.struct.*.SHOW_SEQUENCE     # Not yet working in winbind
 samba4.winbind.struct.*.GETPWENT          # Not yet working in winbind
 samba4.winbind.struct.*.SETPWENT          # Not yet working in winbind
 samba4.winbind.struct.*.LOOKUP_NAME_SID   # Not yet working in winbind
 samba4.winbind.struct.*.LIST_GROUPS
-^samba4.*base.delaywrite.*update of write time and SMBwrite truncate$
-^samba4.*base.delaywrite.*update of write time and SMBwrite truncate expand$
-^samba4.*base.delaywrite.*delayed update of write time 3a$
-^samba4.*base.delaywrite.*delayed update of write time 3c$
-^samba4.*base.delaywrite.*update of write time using SET_END_OF_FILE$
-^samba4.*base.delaywrite.*update of write time using SET_ALLOCATION_SIZE$
-^samba4.ldap.python \(dc\).Test add_ldif\(\) with BASE64 security descriptor input using WRONG domain SID$
+samba4.*base.delaywrite.*update of write time and SMBwrite truncate$
+samba4.*base.delaywrite.*update of write time and SMBwrite truncate expand$
+samba4.*base.delaywrite.*delayed update of write time 3a$
+samba4.*base.delaywrite.*delayed update of write time 3c$
+samba4.*base.delaywrite.*update of write time using SET_END_OF_FILE$
+samba4.*base.delaywrite.*update of write time using SET_ALLOCATION_SIZE$
+samba4.ldap.python \(dc\).Test add_ldif\(\) with BASE64 security descriptor input using WRONG domain SID$
 # some operations don't work over the CIFS NTVFS backend yet (eg. root_fid)
 samba4.ntvfs.cifs.base.createx_access 
 samba4.ntvfs.cifs.base.createx_sharemodes_dir
index f832b4f15ed84067f8d1ac23269ff37a551edb0d..bf6127f4a5a58909945475f2351b23de6fe4e05e 100755 (executable)
@@ -85,7 +85,7 @@ smb4torture="$samba4bindir/smbtorture${EXEEXT}"
 if which tap2subunit 2>/dev/null; then
        TAP2SUBUNIT=tap2subunit
 else
-       TAP2SUBUNIT="PYTHONPATH=$samba4srcdir/../lib/subunit/python $PYTHON $samba4srcdir/../lib/subunit/filters/tap2subunit"
+       TAP2SUBUNIT="PYTHONPATH=$samba4srcdir/../lib/subunit/python:$samba4srcdir/../lib/testtools $PYTHON $samba4srcdir/../lib/subunit/filters/tap2subunit"
 fi
 $smb4torture -V
 
@@ -466,15 +466,15 @@ plantest "samba3sam.python" none PYTHONPATH="$PYTHONPATH:$samba4srcdir/dsdb/samd
 plantest "subunit.python" none $SUBUNITRUN subunit
 plantest "rpcecho.python" dc:local $SUBUNITRUN samba.tests.dcerpc.rpcecho
 plantest "winreg.python" dc:local $SUBUNITRUN -U\$USERNAME%\$PASSWORD samba.tests.dcerpc.registry
-plantest "ldap.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python" $PYTHON $samba4srcdir/lib/ldb/tests/python/ldap.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
-plantest "urgent_replication.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python" $PYTHON $samba4srcdir/lib/ldb/tests/python/urgent_replication.py \$PREFIX_ABS/dc/private/sam.ldb
-plantest "ldap_schema.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python" $PYTHON $samba4srcdir/lib/ldb/tests/python/ldap_schema.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
+plantest "ldap.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python:../lib/testtools" $PYTHON $samba4srcdir/lib/ldb/tests/python/ldap.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
+plantest "urgent_replication.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python:../lib/testtools" $PYTHON $samba4srcdir/lib/ldb/tests/python/urgent_replication.py \$PREFIX_ABS/dc/private/sam.ldb
+plantest "ldap_schema.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python:../lib/testtools" $PYTHON $samba4srcdir/lib/ldb/tests/python/ldap_schema.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
 plantest "ldap.possibleInferiors.python" dc $PYTHON $samba4srcdir/dsdb/samdb/ldb_modules/tests/possibleinferiors.py $CONFIGURATION ldap://\$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
-plantest "ldap.secdesc.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python" $PYTHON $samba4srcdir/lib/ldb/tests/python/sec_descriptor.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
-plantest "ldap.acl.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python" $PYTHON $samba4srcdir/lib/ldb/tests/python/acl.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
+plantest "ldap.secdesc.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python:../lib/testtools" $PYTHON $samba4srcdir/lib/ldb/tests/python/sec_descriptor.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
+plantest "ldap.acl.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python:../lib/testtools" $PYTHON $samba4srcdir/lib/ldb/tests/python/acl.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
 plantest "xattr.python" none $SUBUNITRUN samba.tests.xattr
 plantest "ntacls.python" none $SUBUNITRUN samba.tests.ntacls
-plantest "deletetest.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python" $PYTHON $samba4srcdir/lib/ldb/tests/python/deletetest.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
+plantest "deletetest.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python:../lib/testtools" $PYTHON $samba4srcdir/lib/ldb/tests/python/deletetest.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
 plantest "blackbox.samba3dump" none $PYTHON $samba4srcdir/scripting/bin/samba3dump $samba4srcdir/../testdata/samba3
 rm -rf $PREFIX/upgrade
 plantest "blackbox.upgrade" none $PYTHON $samba4srcdir/setup/upgrade_from_s3 $CONFIGURATION --targetdir=$PREFIX/upgrade $samba4srcdir/../testdata/samba3 ../testdata/samba3/smb.conf
index 81e90f88c82960e6adbb38cd8d44b4d3a61ae0fe..8a5c24ca7949ed3affda114522d6aa390c7c2d33 100644 (file)
@@ -2095,13 +2095,6 @@ static int cxd_find_known(struct createx_data *cxd)
        return -1;
 }
 
-#define FILL_NTCREATEX(_struct, _init...)                            \
-       do {                                                         \
-               (_struct)->generic.level = RAW_OPEN_NTCREATEX;       \
-               (_struct)->ntcreatex.in                              \
-                   = (__typeof__((_struct)->ntcreatex.in)) {_init}; \
-       } while (0)
-
 #define CREATEX_NAME "\\createx_dir"
 
 static bool createx_make_dir(struct torture_context *tctx,
@@ -2124,15 +2117,16 @@ static bool createx_make_file(struct torture_context *tctx,
        bool ret = true;
        NTSTATUS status;
 
-       FILL_NTCREATEX(&open_parms,
-           .flags = 0,
-           .access_mask = SEC_RIGHTS_FILE_ALL,
-           .file_attr = FILE_ATTRIBUTE_NORMAL,
-           .share_access = 0,
-           .open_disposition = NTCREATEX_DISP_CREATE,
-           .create_options = 0,
-           .fname = fname
-       );
+       ZERO_STRUCT(open_parms);
+       open_parms.generic.level = RAW_OPEN_NTCREATEX;
+       open_parms.ntcreatex.in.flags = 0;
+       open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+       open_parms.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       open_parms.ntcreatex.in.share_access = 0;
+       open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+       open_parms.ntcreatex.in.create_options = 0;
+       open_parms.ntcreatex.in.fname = fname;
+
        status = smb_raw_open(tree, mem_ctx, &open_parms);
        CHECK_STATUS(status, NT_STATUS_OK);
 
@@ -2146,30 +2140,30 @@ static bool createx_make_file(struct torture_context *tctx,
 static void createx_fill_dir(union smb_open *open_parms, int accessmode,
     int sharemode, const char *fname)
 {
-       FILL_NTCREATEX(open_parms,
-           .flags = 0,
-           .access_mask = accessmode,
-           .file_attr = FILE_ATTRIBUTE_DIRECTORY,
-           .share_access = sharemode,
-           .open_disposition = NTCREATEX_DISP_OPEN_IF,
-           .create_options = NTCREATEX_OPTIONS_DIRECTORY,
-           .fname = fname
-       );
+       ZERO_STRUCTP(open_parms);
+       open_parms->generic.level = RAW_OPEN_NTCREATEX;
+       open_parms->ntcreatex.in.flags = 0;
+       open_parms->ntcreatex.in.access_mask = accessmode;
+       open_parms->ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
+       open_parms->ntcreatex.in.share_access = sharemode;
+       open_parms->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+       open_parms->ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+       open_parms->ntcreatex.in.fname = fname;
 }
 
 static void createx_fill_file(union smb_open *open_parms, int accessmode,
     int sharemode, const char *fname)
 {
-       FILL_NTCREATEX(open_parms,
-           .flags = 0,
-           .access_mask = accessmode,
-           .file_attr = FILE_ATTRIBUTE_NORMAL,
-           .share_access = sharemode,
-           .open_disposition = NTCREATEX_DISP_OPEN_IF,
-           .create_options = 0,
-           .fname = fname,
-            .root_fid = { .fnum = 0 }
-       );
+       ZERO_STRUCTP(open_parms);
+       open_parms->generic.level = RAW_OPEN_NTCREATEX;
+       open_parms->ntcreatex.in.flags = 0;
+       open_parms->ntcreatex.in.access_mask = accessmode;
+       open_parms->ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       open_parms->ntcreatex.in.share_access = sharemode;
+       open_parms->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+       open_parms->ntcreatex.in.create_options = 0;
+       open_parms->ntcreatex.in.fname = fname;
+       open_parms->ntcreatex.in.root_fid.fnum = 0;
 }
 
 static int data_file_fd = -1;
@@ -2184,15 +2178,16 @@ static bool createx_test_dir(struct torture_context *tctx,
        union smb_open open_parms;
 
        /* bypass original handle to guarantee creation */
-       FILL_NTCREATEX(&open_parms,
-           .flags = 0,
-           .access_mask = SEC_RIGHTS_FILE_ALL,
-           .file_attr = FILE_ATTRIBUTE_NORMAL,
-           .share_access = 0,
-           .open_disposition = NTCREATEX_DISP_CREATE,
-           .create_options = 0,
-           .fname = CREATEX_NAME "\\" KNOWN
-       );
+       ZERO_STRUCT(open_parms);
+       open_parms.generic.level = RAW_OPEN_NTCREATEX;
+       open_parms.ntcreatex.in.flags = 0;
+       open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+       open_parms.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       open_parms.ntcreatex.in.share_access = 0;
+       open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+       open_parms.ntcreatex.in.create_options = 0;
+       open_parms.ntcreatex.in.fname = CREATEX_NAME "\\" KNOWN;
+
        status = smb_raw_open(tree, mem_ctx, &open_parms);
        CHECK_STATUS(status, NT_STATUS_OK);
        smbcli_close(tree, open_parms.ntcreatex.out.file.fnum);
@@ -2200,32 +2195,32 @@ static bool createx_test_dir(struct torture_context *tctx,
        result[CXD_DIR_ENUMERATE] = NT_STATUS_OK;
 
        /* try to create a child */
-       FILL_NTCREATEX(&open_parms,
-           .flags = 0,
-           .access_mask = SEC_RIGHTS_FILE_ALL,
-           .file_attr = FILE_ATTRIBUTE_NORMAL,
-           .share_access = 0,
-           .open_disposition = NTCREATEX_DISP_CREATE,
-           .create_options = 0,
-           .fname = CHILD,
-           .root_fid = { .fnum = fnum }
-       );
+       ZERO_STRUCT(open_parms);
+       open_parms.generic.level = RAW_OPEN_NTCREATEX;
+       open_parms.ntcreatex.in.flags = 0;
+       open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+       open_parms.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       open_parms.ntcreatex.in.share_access = 0;
+       open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+       open_parms.ntcreatex.in.create_options = 0;
+       open_parms.ntcreatex.in.fname = CHILD;
+       open_parms.ntcreatex.in.root_fid.fnum = fnum;
 
        result[CXD_DIR_CREATE_CHILD] =
            smb_raw_open(tree, mem_ctx, &open_parms);
        smbcli_close(tree, open_parms.ntcreatex.out.file.fnum);
 
        /* try to traverse dir to known good file */
-       FILL_NTCREATEX(&open_parms,
-           .flags = 0,
-           .access_mask = SEC_RIGHTS_FILE_ALL,
-           .file_attr = FILE_ATTRIBUTE_NORMAL,
-           .share_access = 0,
-           .open_disposition = NTCREATEX_DISP_OPEN,
-           .create_options = 0,
-           .fname = KNOWN,
-           .root_fid = {.fnum = fnum}
-       );
+       ZERO_STRUCT(open_parms);
+       open_parms.generic.level = RAW_OPEN_NTCREATEX;
+       open_parms.ntcreatex.in.flags = 0;
+       open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+       open_parms.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       open_parms.ntcreatex.in.share_access = 0;
+       open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+       open_parms.ntcreatex.in.create_options = 0;
+       open_parms.ntcreatex.in.fname = KNOWN;
+       open_parms.ntcreatex.in.root_fid.fnum = fnum;
 
        result[CXD_DIR_TRAVERSE] =
            smb_raw_open(tree, mem_ctx, &open_parms);
index e37fd8e09a2354273462def43f561735b3b2fd4d..42f693e78ce4cc4de4106c5b5b749f876cee2cd4 100644 (file)
@@ -1787,17 +1787,9 @@ done:
        return ret;
 }
 
-#define FILL_NTCREATEX(_struct, _init...)                       \
-       do {                                                    \
-               (_struct)->generic.level = RAW_OPEN_NTCREATEX;  \
-               (_struct)->ntcreatex.in                         \
-                   = (typeof((_struct)->ntcreatex.in)) {_init};\
-       } while (0)
-
 static bool test_ntcreatex_opendisp_dir(struct torture_context *tctx,
                                        struct smbcli_state *cli)
 {
-       union smb_open io;
        const char *dname = BASEDIR "\\torture_ntcreatex_opendisp_dir";
        NTSTATUS status;
        bool ret = true;
@@ -1822,20 +1814,21 @@ static bool test_ntcreatex_opendisp_dir(struct torture_context *tctx,
                { 6,                            true,  NT_STATUS_INVALID_PARAMETER },
                { 6,                            false, NT_STATUS_INVALID_PARAMETER },
        };
+       union smb_open io;
+
+       ZERO_STRUCT(io);
+       io.generic.level = RAW_OPEN_NTCREATEX;
+       io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+       io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+       io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
+       io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+       io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+       io.ntcreatex.in.fname = dname;
 
        if (!torture_setup_dir(cli, BASEDIR)) {
                return false;
        }
 
-       FILL_NTCREATEX(&io,
-           .flags = NTCREATEX_FLAGS_EXTENDED,
-           .access_mask = SEC_FLAG_MAXIMUM_ALLOWED,
-           .file_attr = FILE_ATTRIBUTE_DIRECTORY,
-           .share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE,
-           .create_options = NTCREATEX_OPTIONS_DIRECTORY,
-           .fname = dname,
-       );
-
        smbcli_rmdir(cli->tree, dname);
        smbcli_unlink(cli->tree, dname);
 
index 53e860a144cdd75dcbeb6db3b898226a1ae9c312..2aa340ee36988dc92fd4e4b767b72dd9a57f3d83 100644 (file)
@@ -354,12 +354,18 @@ static void simple_warning(struct torture_context *test,
        fprintf(stderr, "WARNING: %s\n", comment);
 }
 
+static void simple_progress(struct torture_context *test,
+       int offset, enum torture_progress_whence whence)
+{
+}
+
 const static struct torture_ui_ops std_ui_ops = {
        .comment = simple_comment,
        .warning = simple_warning,
        .suite_start = simple_suite_start,
        .suite_finish = simple_suite_finish,
-       .test_result = simple_test_result
+       .test_result = simple_test_result,
+       .progress = simple_progress,
 };